kilim.mirrors.Detector Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kilim Show documentation
Show all versions of kilim Show documentation
Coroutines, continuations, fibers, actors and message passing for the JVM
/* Copyright (c) 2006, Sriram Srinivasan
*
* You may distribute this software under the terms of the license
* specified in the file "License"
*/
package kilim.mirrors;
import static kilim.Constants.D_OBJECT;
import java.util.ArrayList;
import kilim.NotPausable;
import kilim.Pausable;
import kilim.analysis.AsmDetector;
import kilim.mirrors.CachedClassMirrors.ClassMirror;
import kilim.mirrors.CachedClassMirrors.MethodMirror;
/**
* Utility class to check if a method has been marked pausable
*
*/
public class Detector {
public static final int METHOD_NOT_FOUND_OR_PAUSABLE = 0; // either not found, or not pausable if found.
public static final int PAUSABLE_METHOD_FOUND = 1; // known to be pausable
public static final int METHOD_NOT_PAUSABLE = 2; // known to be not pausable
// Note that we don't have the kilim package itself in the following list.
static final String[] STANDARD_DONT_CHECK_LIST = { "java.", "javax." };
public final CachedClassMirrors mirrors;
public Detector(CachedClassMirrors mirrors) {
this.mirrors = mirrors;
NOT_PAUSABLE = mirrors.mirror(NotPausable.class);
PAUSABLE = mirrors.mirror(Pausable.class);
OBJECT = mirrors.mirror(Object.class);
}
ClassMirror NOT_PAUSABLE, PAUSABLE, OBJECT;
public boolean isPausable(String className, String methodName, String desc) {
return getPausableStatus(className, methodName, desc) == PAUSABLE_METHOD_FOUND;
}
/**
* @return one of METHOD_NOT_FOUND, PAUSABLE_METHOD_FOUND, METHOD_NOT_PAUSABLE
*/
static boolean isNonPausableClass(String className) {
return className == null || className.charAt(0) == '[' ||
className.startsWith("java.") || className.startsWith("javax.");
}
static boolean isNonPausableMethod(String methodName) {
return methodName.endsWith("init>");
}
public int getPausableStatus(String className, String methodName, String desc) {
int ret = METHOD_NOT_FOUND_OR_PAUSABLE;
// array methods (essentially methods deferred to Object (clone, wait etc)
// and constructor methods are not pausable
if (isNonPausableClass(className) || isNonPausableMethod(methodName)) {
return METHOD_NOT_FOUND_OR_PAUSABLE;
}
className = className.replace('/', '.');
try {
MethodMirror m = findPausableMethod(className, methodName, desc);
if (m != null) {
for (String ex : m.getExceptionTypes()) {
if (isNonPausableClass(ex)) continue;
ClassMirror c = classForName(ex);
if (NOT_PAUSABLE.isAssignableFrom(c)) {
return METHOD_NOT_PAUSABLE;
}
if (PAUSABLE.isAssignableFrom(c)) {
return PAUSABLE_METHOD_FOUND;
}
}
return METHOD_NOT_PAUSABLE;
}
} catch (ClassMirrorNotFoundException ignore) {
} catch (VerifyError ve) {
return AsmDetector.getPausableStatus(className, methodName, desc, this);
}
return ret;
}
public ClassMirror classForName(String className) throws ClassMirrorNotFoundException {
className = className.replace('/', '.');
return mirrors.classForName(className);
}
public ClassMirror[] classForNames(String[] classNames) throws ClassMirrorNotFoundException {
if (classNames == null) {
return new ClassMirror[0];
}
ClassMirror[] ret = new ClassMirror[classNames.length];
int i = 0;
for (String cn : classNames) {
ret[i++] = classForName(cn);
}
return ret;
}
private MethodMirror findPausableMethod(String className, String methodName, String desc)
throws ClassMirrorNotFoundException {
if (isNonPausableClass(className) || isNonPausableMethod(methodName))
return null;
ClassMirror cl = classForName(className);
if (cl == null) return null;
for (MethodMirror om : cl.getDeclaredMethods()) {
if (om.getName().equals(methodName)) {
// when comparing descriptors only compare arguments, not return types
String omDesc= om.getMethodDescriptor();
if (omDesc.substring(0,omDesc.indexOf(")")).equals(desc.substring(0,desc.indexOf(")")))) {
if (om.isBridge()) continue;
return om;
}
}
}
if (OBJECT.equals(cl))
return null;
MethodMirror m = findPausableMethod(cl.getSuperclass(), methodName, desc);
if (m != null)
return m;
for (String ifname : cl.getInterfaces()) {
if (isNonPausableClass(ifname)) continue;
m = findPausableMethod(ifname, methodName, desc);
if (m != null)
return m;
}
return null;
}
@SuppressWarnings("unused")
private static String statusToStr(int st) {
switch (st) {
case METHOD_NOT_FOUND_OR_PAUSABLE:
return "not found or pausable";
case PAUSABLE_METHOD_FOUND:
return "pausable";
case METHOD_NOT_PAUSABLE:
return "not pausable";
default:
throw new AssertionError("Unknown status");
}
}
public String commonSuperType(String oa, String ob) throws ClassMirrorNotFoundException {
String a = toClassName(oa);
String b = toClassName(ob);
try {
ClassMirror ca = classForName(a);
ClassMirror cb = classForName(b);
if (ca.isAssignableFrom(cb))
return oa;
if (cb.isAssignableFrom(ca))
return ob;
if (ca.isInterface() && cb.isInterface()) {
return "java/lang/Object"; // This is what the java bytecode verifier does
}
} catch (ClassMirrorNotFoundException e) {
// try to see if the below works...
}
if (a.equals(b)) {
return oa;
}
ArrayList sca = getSuperClasses(a);
ArrayList scb = getSuperClasses(b);
int lasta = sca.size() - 1;
int lastb = scb.size() - 1;
do {
if (sca.get(lasta).equals(scb.get(lastb))) {
lasta--;
lastb--;
} else {
break;
}
} while (lasta >= 0 && lastb >= 0);
if (sca.size() == lasta+1) {
return "java/lang/Object";
}
return sca.get(lasta + 1).replace('.', '/');
}
final private static ArrayList EMPTY_STRINGS = new ArrayList(0);
public ArrayList getSuperClasses(String name) throws ClassMirrorNotFoundException {
if (name == null) {
return EMPTY_STRINGS;
}
ArrayList ret = new ArrayList(3);
while (name != null) {
ret.add(name);
ClassMirror c = classForName(name);
name = c.getSuperclass();
}
return ret;
}
private static String toDesc(String name) {
return (name.equals(JAVA_LANG_OBJECT)) ? D_OBJECT : "L" + name.replace('.', '/') + ';';
}
private static String toClassName(String s) {
if (s.endsWith(";"))
return s.replace('/', '.').substring(1, s.length() - 1);
else
return s.replace('/', '.');
}
static String JAVA_LANG_OBJECT = "java.lang.Object";
}