/* 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 java.util.HashSet;
import java.util.Set;
import kilim.Constants;
import kilim.NotPausable;
import kilim.Pausable;
import kilim.analysis.AsmDetector;
/**
* 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 static final Detector DEFAULT = new Detector(new RuntimeClassMirrors());
public final Mirrors mirrors;
public Detector(Mirrors 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;
}
public static String D_FIBER_ = Constants.D_FIBER + ")";
@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");
}
}
static private final ThreadLocal<Detector> DETECTOR = new ThreadLocal<Detector>();
public static Detector getDetector() {
Detector d = DETECTOR.get();
if (d == null)
return Detector.DEFAULT;
return d;
}
public static Detector setDetector(Detector d) {
Detector res = DETECTOR.get();
DETECTOR.set(d);
return res;
}
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<String> sca = getSuperClasses(a);
ArrayList<String> 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<String> EMPTY_STRINGS = new ArrayList<String>(0);
public ArrayList<String> getSuperClasses(String name) throws ClassMirrorNotFoundException {
if (name == null) {
return EMPTY_STRINGS;
}
ArrayList<String> ret = new ArrayList<String>(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";
}