package kilim.analysis;
import java.util.LinkedList;
import java.io.IOException;
import java.util.HashMap;
import kilim.mirrors.Detector;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
/**
* This class is called by Detector to parse signatures of classes
* that may have verification errors. It uses asm to open the file instead
* of trying to classload it.
*/
public class AsmDetector {
static HashMap<String, ClassCache> classCacheMap= new HashMap<String, ClassCache>();
public static int getPausableStatus(String className, String methodName,
String desc, Detector detector)
{
try {
ClassCache classCache = classCacheMap.get(className);
if (classCache == null) {
ClassReader cr = new ClassReader(className);
ClassNode cn = new ClassNode();
cr.accept(cn, /*flags*/ 0);
classCache = cache(className, cn);
}
int status = classCache.getPausableStatus(methodName, desc);
if (status == Detector.METHOD_NOT_FOUND_OR_PAUSABLE) {
// check super classes
for (String superName: classCache.superClasses) {
status = detector.getPausableStatus(superName, methodName, desc);
if (status != Detector.METHOD_NOT_FOUND_OR_PAUSABLE)
break;
}
}
return status;
} catch (IOException ioe) {
System.err.println("***Error reading " + className + ": " + ioe.getMessage());
return Detector.METHOD_NOT_FOUND_OR_PAUSABLE;
}
}
private static ClassCache cache(String className, ClassNode cn) {
ClassCache classCache = new ClassCache();
classCache.className = className;
classCacheMap.put(className, classCache);
LOOP:
for (Object m: cn.methods) {
MethodNode mn = (MethodNode)m;
for (Object exception: mn.exceptions) {
if ("kilim/Pausable".equals(exception)) {
classCache.pausableMethods.add(mn.name + mn.desc);
continue LOOP;
}
}
classCache.otherMethods.add(mn.name + mn.desc);
}
classCache.addSuper(cn.superName);
for (Object interfaceName: cn.interfaces) {
classCache.addSuper((String)interfaceName);
}
// System.out.println(classCache);
return classCache;
}
public static void main(String[] args) {
AsmDetector.getPausableStatus("com/sleepycat/je/Database", "putInternal", "Lcom/sleepycat/je/Transaction;Lcom/sleepycat/je/DatabaseEntry;Lcom/sleepycat/je/DatabaseEntry;Lcom/sleepycat/je/dbi/PutMode;Lkilim/Fiber;)Lcom/sleepycat/je/OperationStatus;)V", Detector.DEFAULT);
}
static class ClassCache {
String className;
LinkedList<String> pausableMethods = new LinkedList<String>();
LinkedList<String> otherMethods = new LinkedList<String>();
LinkedList<String> superClasses = new LinkedList<String>();
public void addSuper(String superName) {
if (superName.equals("java/lang/Object")) return;
if (!superClasses.contains(superName)) {superClasses.add(superName);}
}
public int getPausableStatus(String methodName, String desc) {
String md = methodName + desc;
if (pausableMethods.contains(md)) {
return Detector.PAUSABLE_METHOD_FOUND;
} else if (otherMethods.contains(md)) {
return Detector.METHOD_NOT_PAUSABLE;
} else {
return Detector.METHOD_NOT_FOUND_OR_PAUSABLE;
}
}
@Override
public String toString() {
return className + "\nPausable Methods: " + pausableMethods + "\nOthers:" + otherMethods;
}
}
}