Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

We’re showing branches in this repository, but you can also compare across forks.

base fork: kilim/kilim
base: 91e35b3015
...
head fork: kilim/kilim
compare: 1b54ce98df
  • 8 commits
  • 36 files changed
  • 0 commit comments
  • 1 contributor
Commits on May 03, 2011
Sriram Srinivasan 1. Moved Detector from kilim.analysis to kilim.mirrors to avoid inter…
…-package

   recursive calling.  That accounts for many of the changes in kilim.analysis.

2. Removed Field/MemberMirror because the weaver doesn't need access to fields.

3. Created two parallel hierarchies as a first step towards runtime weaving:
   Cached{Class,Method}Mirror  to represent class files supplied to the weaver
   that are not in the classpath (or loaded by the context classloader)
   and Runtime{Class,Method}Mirror for those that are. This commit has not
   tested the Cached*Mirror functionality.
52596c3
Commits on May 04, 2011
Sriram Srinivasan See previous commit log. Missed two new files.
	new file:   src/kilim/mirrors/CachedClassMirrors.java
	new file:   src/kilim/mirrors/Detector.java
9972867
Commits on May 05, 2011
Sriram Srinivasan Working version of run-time weaving. See Weaver.weave(List<ClassInfo>) 247f18c
Commits on May 07, 2011
Sriram Srinivasan Dynamic Weaving enabled: classes are woven at run time as required.
Introduced a driver kilim.tools.Kilim that launches the supplied classname
under the aegis of a WeavingClassLoader.
c95623b
Commits on May 08, 2011
Sriram Srinivasan All the changes stem from modifications to ClassMirror, MethodMirror.
Their methods no longer return other ClassMirror objects in response to getSuperClass()
or getInterfaces() or getExceptions(). Instead, they return String or String[]. Detector looks up those classes as necessary. This change avoids mirroring classes that we
KNOW to be non pausable, those in "java." or "javax." packages.

Weaving is also faster because the weaver stops looking for a pausable method up in the hierarchy as soon as it encounters a super class name in one of these nonpausable packages.
0e50160
Sriram Srinivasan Added jar support to WeavingClassLoader
	modified:   src/kilim/WeavingClassLoader.java
	modified:   src/kilim/analysis/FileLister.java
63b5ea7
Sriram Srinivasan FileLister holds on to the embedded FileContainer via a WeakReference…
… now to avoid holding on to an open jar file under memory pressure

	modified:   src/kilim/analysis/FileLister.java
4afe3bf
Commits on Oct 05, 2011
Sriram Srinivasan Resolution to github issue #8.
Problem: In Frame.merge the operand stacks were being examined pointwise for differences, but due to an incorrect comparison, the analysis assumed that the stack had not changed.

TestYield/ExLoop are the relevant unit test and example code to exercise this functionality.

	modified:   .classpath
	modified:   src/kilim/analysis/Frame.java
	modified:   src/kilim/analysis/Usage.java
	modified:   src/kilim/tools/Asm.java
	modified:   src/kilim/tools/P.java
	modified:   test/kilim/test/TestYield.java
	new file:   test/kilim/test/ex/ExLoop.java
	modified:   test/kilim/test/ex/ExYieldStack.java
1b54ce9
Showing with 1,568 additions and 638 deletions.
  1. +1 −1  .classpath
  2. +18 −0 src/kilim/KilimClassLoader.java
  3. +99 −0 src/kilim/WeavingClassLoader.java
  4. +6 −4 src/kilim/analysis/AsmDetector.java
  5. +1 −0  src/kilim/analysis/BasicBlock.java
  6. +2 −1  src/kilim/analysis/ClassFlow.java
  7. +14 −1 src/kilim/analysis/ClassInfo.java
  8. +17 −7 src/kilim/analysis/ClassWeaver.java
  9. +0 −205 src/kilim/analysis/Detector.java
  10. +76 −12 src/kilim/analysis/FileLister.java
  11. +2 −2 src/kilim/analysis/Frame.java
  12. +2 −3 src/kilim/analysis/MethodFlow.java
  13. +135 −107 src/kilim/analysis/TypeDesc.java
  14. +35 −31 src/kilim/analysis/Usage.java
  15. +223 −0 src/kilim/mirrors/CachedClassMirrors.java
  16. +3 −3 src/kilim/mirrors/ClassMirror.java
  17. +3 −0  src/kilim/mirrors/ClassMirrorNotFoundException.java
  18. +233 −0 src/kilim/mirrors/Detector.java
  19. +0 −7 src/kilim/mirrors/FieldMirror.java
  20. +0 −5 src/kilim/mirrors/MemberMirror.java
  21. +4 −2 src/kilim/mirrors/MethodMirror.java
  22. +6 −14 src/kilim/mirrors/Mirrors.java
  23. +185 −178 src/kilim/mirrors/RuntimeClassMirrors.java
  24. +13 −12 src/kilim/tools/Asm.java
  25. +1 −1  src/kilim/tools/FlowAnalyzer.java
  26. +175 −0 src/kilim/tools/Javac.java
  27. +40 −0 src/kilim/tools/Kilim.java
  28. +10 −5 src/kilim/tools/P.java
  29. +120 −35 src/kilim/tools/Weaver.java
  30. +1 −0  test/kilim/test/AllNotWoven.java
  31. +1 −1  test/kilim/test/Base.java
  32. +97 −0 test/kilim/test/TestDynamicWeaver.java
  33. +1 −1  test/kilim/test/TestInvalidPausables.java
  34. +7 −0 test/kilim/test/TestYield.java
  35. +30 −0 test/kilim/test/ex/ExLoop.java
  36. +7 −0 test/kilim/test/ex/ExYieldStack.java
2  .classpath
View
@@ -5,7 +5,7 @@
<classpathentry excluding="scala/" kind="src" path="bench"/>
<classpathentry kind="src" path="test"/>
<classpathentry kind="lib" path="libs/asm-all-2.2.3.jar" sourcepath="/Users/s/sw/asm-2.2.3/src"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="libs/junit.jar"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.launching.macosx.MacOSXType/1.6.0"/>
<classpathentry kind="output" path="classes"/>
</classpath>
18 src/kilim/KilimClassLoader.java
View
@@ -0,0 +1,18 @@
+package kilim;
+
+/**
+ * Extends Classloader just to have access to the (protected) findLoadedClass method
+ */
+public class KilimClassLoader extends ClassLoader {
+ public KilimClassLoader(ClassLoader cl) {
+ super(cl);
+ }
+
+ public Class<?> getLoadedClass(String className) {
+ return super.findLoadedClass(className);
+ }
+
+ public boolean isLoaded(String className) {
+ return getLoadedClass(className) != null;
+ }
+}
99 src/kilim/WeavingClassLoader.java
View
@@ -0,0 +1,99 @@
+package kilim;
+
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import kilim.analysis.ClassInfo;
+import kilim.analysis.FileLister;
+import kilim.tools.Weaver;
+
+/**
+ * Classloader that loads classes from the classpath spec given by the system property
+ * "kilim.class.path" and weaves them dynamically.
+ */
+public class WeavingClassLoader extends KilimClassLoader {
+ public static final String KILIM_CLASSPATH = "kilim.class.path";
+ /**
+ * List of paths in kilim.class.path
+ */
+ ArrayList<FileLister> fileContainers;
+ /**
+ * Weaver instance. There is a mutually recursive dependency between the weaver and
+ * this class loader. See {@link #findClass(String)}
+ */
+ Weaver weaver;
+
+ public WeavingClassLoader(ClassLoader parent) {
+ super(parent);
+ String classPath = System.getProperty(KILIM_CLASSPATH, "");
+ String[] classPaths = classPath.split(":");
+ fileContainers = new ArrayList<FileLister>(classPaths.length);
+ for (String name : classPaths) {
+ name = name.trim();
+ if (name.equals(""))
+ continue;
+ try {
+ fileContainers.add(new FileLister(name));
+ } catch (IOException ioe) {
+ // System.err.println( "'" + name + "' does not exist. See property " +
+ // KILIM_CLASSPATH);
+ }
+ }
+ weaver = new Weaver(this); // mutually recursive dependency.
+ }
+
+
+ /**
+ * Check if class file exists in kilim.class.path.
+ */
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ Class<?> ret = null;
+ for (FileLister container : fileContainers) {
+ try {
+ String classFileName = name.replace('.', File.separatorChar) + ".class";
+ FileLister.Entry fe = container.open(classFileName);
+ if (fe == null) continue;
+ byte[] code = readFully(fe);
+ List<ClassInfo> cis = weaver.weave(new ClassInfo(name, code));
+
+ for (ClassInfo ci : cis) {
+ if (findLoadedClass(ci.className) != null)
+ continue;
+ Class<?> c = super.defineClass(ci.className, ci.bytes, 0, ci.bytes.length);
+ if (ci.className.equals(name)) {
+ ret = c;
+ } else {
+ // extra classes produced by the weaver. resolve them right away
+ // That way, when the given class name is resolved, it'll find its
+ // kilim related state object classes right away.
+ if (ci.className.startsWith("kilim.S")) {
+ super.resolveClass(c);
+ }
+ }
+ }
+ if (ret == null) {
+ // code exists, but didn't need to be woven
+ ret = super.defineClass(name, code, 0, code.length);
+ }
+ } catch (IOException ignore) {
+ System.err.println(ignore.getMessage());
+ }
+ }
+ if (ret == null) {
+ throw new ClassNotFoundException(name);
+ }
+ return ret;
+ }
+
+ private static byte[] readFully(FileLister.Entry fe) throws IOException {
+ DataInputStream in = new DataInputStream(fe.getInputStream());
+ byte[] contents = new byte[(int)fe.getSize()];
+ in.readFully(contents);
+ in.close();
+ return contents;
+ }
+}
10 src/kilim/analysis/AsmDetector.java
View
@@ -4,6 +4,8 @@
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;
@@ -27,18 +29,18 @@ public static int getPausableStatus(String className, String methodName,
classCache = cache(className, cn);
}
int status = classCache.getPausableStatus(methodName, desc);
- if (status == Detector.METHOD_NOT_FOUND) {
+ 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)
+ 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;
+ return Detector.METHOD_NOT_FOUND_OR_PAUSABLE;
}
}
private static ClassCache cache(String className, ClassNode cn) {
@@ -83,7 +85,7 @@ public int getPausableStatus(String methodName, String desc) {
} else if (otherMethods.contains(md)) {
return Detector.METHOD_NOT_PAUSABLE;
} else {
- return Detector.METHOD_NOT_FOUND;
+ return Detector.METHOD_NOT_FOUND_OR_PAUSABLE;
}
}
@Override
1  src/kilim/analysis/BasicBlock.java
View
@@ -36,6 +36,7 @@
import java.util.Stack;
import kilim.KilimException;
+import kilim.mirrors.Detector;
import org.objectweb.asm.Label;
import org.objectweb.asm.tree.AbstractInsnNode;
3  src/kilim/analysis/ClassFlow.java
View
@@ -6,6 +6,7 @@
package kilim.analysis;
import kilim.*;
+import kilim.mirrors.Detector;
import java.io.IOException;
import java.io.InputStream;
@@ -52,6 +53,7 @@ public ClassFlow(byte[] data, Detector detector) {
this.detector = detector;
}
+
@Override
@SuppressWarnings( { "unchecked" })
public MethodVisitor visitMethod(
@@ -77,7 +79,6 @@ public MethodVisitor visitMethod(
Detector save = Detector.setDetector(detector);
try {
-
cr.accept(this, false);
for (Object o : this.fields) {
FieldNode fn = (FieldNode) o;
15 src/kilim/analysis/ClassInfo.java
View
@@ -6,11 +6,24 @@
package kilim.analysis;
public class ClassInfo {
+ /**
+ * fully qualified classname in a format suitable for Class.forName
+ */
public String className;
+
+ /**
+ * bytecode for the class
+ */
public byte[] bytes;
public ClassInfo(String aClassName, byte[] aBytes) {
- className = aClassName.replace('.', '/');
+ className = aClassName.replace('/', '.');
+// className = aClassName.replace('.', '/');
bytes = aBytes;
}
+
+ @Override
+ public String toString() {
+ return className;
+ }
}
24 src/kilim/analysis/ClassWeaver.java
View
@@ -5,14 +5,26 @@
*/
package kilim.analysis;
-import kilim.*;
-import static kilim.Constants.*;
+import static kilim.Constants.ALOAD_0;
+import static kilim.Constants.D_FIBER;
+import static kilim.Constants.STATE_CLASS;
+import static kilim.Constants.WOVEN_FIELD;
+import static org.objectweb.asm.Opcodes.ACC_FINAL;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static org.objectweb.asm.Opcodes.ACC_STATIC;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.RETURN;
+import static org.objectweb.asm.Opcodes.V1_1;
+
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import kilim.KilimException;
+import kilim.mirrors.Detector;
+
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
@@ -27,7 +39,7 @@
* CPS transformed file if needed
*/
public class ClassWeaver {
- ClassFlow classFlow;
+ public ClassFlow classFlow;
List<ClassInfo> classInfoList = new LinkedList<ClassInfo>();
static HashSet<String> stateClasses = new HashSet<String>();
@@ -37,20 +49,17 @@ public ClassWeaver(byte[] data) {
public ClassWeaver(byte[] data, Detector detector) {
classFlow = new ClassFlow(data, detector);
- weave();
}
public ClassWeaver(InputStream is, Detector detector) throws IOException {
classFlow = new ClassFlow(is, detector);
- weave();
}
public ClassWeaver(String className, Detector detector) throws IOException {
classFlow = new ClassFlow(className, detector);
- weave();
}
- private void weave() throws KilimException {
+ public void weave() throws KilimException {
classFlow.analyze(false);
if (needsWeaving() && classFlow.isPausable()) {
ClassWriter cw = new ClassWriter(false);
@@ -58,6 +67,7 @@ private void weave() throws KilimException {
addClassInfo(new ClassInfo(classFlow.getClassName(), cw.toByteArray()));
}
}
+
private void accept(final ClassVisitor cv) {
ClassFlow cf = classFlow;
205 src/kilim/analysis/Detector.java
View
@@ -1,205 +0,0 @@
-/* Copyright (c) 2006, Sriram Srinivasan
- *
- * You may distribute this software under the terms of the license
- * specified in the file "License"
- */
-package kilim.analysis;
-import static kilim.Constants.D_OBJECT;
-
-import java.util.ArrayList;
-
-import kilim.Constants;
-import kilim.NotPausable;
-import kilim.Pausable;
-import kilim.mirrors.ClassMirror;
-import kilim.mirrors.ClassMirrorNotFoundException;
-import kilim.mirrors.MethodMirror;
-import kilim.mirrors.Mirrors;
-/**
- * Utility class to check if a method has been marked pausable
- *
- */
-public class Detector {
- public static final int METHOD_NOT_FOUND = 0;
- public static final int PAUSABLE_METHOD_FOUND = 1;
- public static final int METHOD_NOT_PAUSABLE = 2;
-
- // 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(Mirrors.getRuntimeMirrors());
-
- private 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
- */
-
- public int getPausableStatus(String className, String methodName,
- String desc)
- {
- int ret = METHOD_NOT_FOUND;
- if (methodName.endsWith("init>")) {
- return METHOD_NOT_PAUSABLE; // constructors are not pausable.
- }
- className = className.replace('/', '.');
- try {
- ClassMirror cl = mirrors.classForName(className);
- MethodMirror m = findMethod(cl, methodName, desc);
- if (m != null) {
- for (ClassMirror c: m.getExceptionTypes()) {
- 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;
- }
-
- private MethodMirror findMethod(ClassMirror cl, String methodName, String desc) {
- if (cl == null) return null;
- MethodMirror m = findMethodInHierarchy(cl, methodName, desc);
- if (m == null) {
- cl = mirrors.mirror(Object.class);
- for (MethodMirror om : cl.getDeclaredMethods()) {
- if (om.getName().equals(methodName) && om.getMethodDescriptor().equals(desc)) {
- return om;
- }
- }
- }
- return m;
- }
-
- private MethodMirror findMethodInHierarchy(ClassMirror cl, String methodName,
- String desc) {
- if (cl == null) return null;
-
- for (MethodMirror om : cl.getDeclaredMethods()) {
- if (om.getName().equals(methodName) && om.getMethodDescriptor().equals(desc)) {
- if (om.isBridge()) continue;
- return om;
- }
- }
-
- if (OBJECT.equals(cl))
- return null;
-
- MethodMirror m = findMethodInHierarchy(cl.getSuperclass(), methodName, desc);
- if (m != null)
- return m;
- for (ClassMirror ifcl : cl.getInterfaces()) {
- m = findMethodInHierarchy(ifcl, 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 : return "not found";
- 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>();
-
- static Detector getDetector() {
- Detector d = DETECTOR.get();
- if (d == null) return Detector.DEFAULT;
- return d;
-}
-
- 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 = mirrors.classForName(a);
- ClassMirror cb = mirrors.classForName(b);
- if (ca.isAssignableFrom(cb)) return oa;
- if (cb.isAssignableFrom(ca)) return ob;
- if (ca.isInterface() && cb.isInterface()) {
- return D_OBJECT; // This is what the java bytecode verifier does
- }
- } catch (ClassMirrorNotFoundException e) {
- // try to see if the below works...
- }
-
- 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);
- return toDesc(sca.get(lasta+1));
- }
-
-
- public ArrayList<String> getSuperClasses(String cc) throws ClassMirrorNotFoundException {
- ClassMirror c = mirrors.classForName(cc);
- ArrayList<String> ret = new ArrayList<String>(3);
- while (c != null) {
- ret.add(c.getName());
- c = 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) {
- return s.replace('/','.').substring(1,s.length()-1);
- }
-
- static String JAVA_LANG_OBJECT = "java.lang.Object";
-
-}
88 src/kilim/analysis/FileLister.java
View
@@ -10,57 +10,99 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.ref.WeakReference;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Stack;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
-/**
- * Utility class to paper over the differences between jar files and
- * directories
+/**
+ * Utility class to present a uniform iterator interface for file containers; presently
+ * includes directories and jar files.
*/
+
public class FileLister implements Iterable<FileLister.Entry> {
public static abstract class Entry {
public abstract String getFileName();
+ public abstract long getSize();
public abstract InputStream getInputStream() throws IOException;
};
- Iterator<FileLister.Entry> iter;
+ /**
+ * weak ref to a container to avoid hanging on to an open jar file.
+ */
+ volatile WeakReference<FileContainer> containerRef;
+ String name;
public FileLister(String dirOrJarName) throws IOException {
- if (dirOrJarName.endsWith(".jar")) {
- iter = openJar(dirOrJarName);
+ name= dirOrJarName;
+ }
+
+ /**
+ * @param relativeFileName
+ * @return if the relativeFileName exists in the directory or jar represented by FileLister object
+ * open it. If not return null.
+ * @throws IOException
+ */
+ public Entry open(String relativeFileName) throws IOException {
+ return getContainer().open(relativeFileName);
+ }
+
+ // Lazily initialize the container.
+ private FileContainer getContainer() throws IOException {
+ FileContainer container = null;
+ if (containerRef != null) {
+ container = containerRef.get();
+ if (container != null) return container;
+ }
+
+ if (name.endsWith(".jar")) {
+ container = openJar(this.name);
} else {
- File f = new File(dirOrJarName);
+ File f = new File(this.name);
if (f.exists() && f.isDirectory()) {
- iter = new DirIterator(f);
+ container = new DirIterator(f);
} else {
throw new IOException("Expected jar file or directory name");
}
}
+ containerRef = new WeakReference<FileContainer>(container);
+ return container;
}
- private Iterator<FileLister.Entry> openJar(String jarFile) throws IOException {
+ private FileContainer openJar(String jarFile) throws IOException {
return new JarIterator(new JarFile(jarFile));
}
public Iterator<FileLister.Entry> iterator() {
- return iter;
+ try {
+ return getContainer();
+ } catch (IOException ignore) {}
+ return null;
}
}
+abstract class FileContainer implements Iterator<FileLister.Entry> {
+ abstract FileLister.Entry open(String relativeFileName) throws IOException;
+}
+
/**
* Preorder traversal of a directory. Returns everything including directory
* names.
*/
-class DirIterator implements Iterator<FileLister.Entry> {
+class DirIterator extends FileContainer {
+ final File root;
private static class DirEntry extends FileLister.Entry {
final File file;
DirEntry(File f) {file = f;}
@Override
+ public long getSize() {
+ return file.length();
+ }
+ @Override
public String getFileName() {
try {
return file.getCanonicalPath();
@@ -76,7 +118,9 @@ public InputStream getInputStream() throws IOException {
Stack<File> stack = new Stack<File>();
+
DirIterator(File f) {
+ root = f;
stack.push(f);
}
@@ -110,9 +154,18 @@ public boolean hasNext() {
public void remove() {
throw new RuntimeException("FileLister does not remove files");
}
+
+ @Override
+ FileLister.Entry open(String fileName) throws IOException {
+ File ret = new File(root.getAbsolutePath() + File.separatorChar + fileName);
+ if (ret.exists() && ret.isFile()) {
+ return new DirEntry(ret);
+ }
+ return null;
+ }
}
-class JarIterator implements Iterator<FileLister.Entry> {
+class JarIterator extends FileContainer {
Enumeration<JarEntry> jarEnum;
JarFile jarFile;
String nextName;
@@ -130,6 +183,11 @@ public String getFileName() {
public InputStream getInputStream() throws IOException {
return jarFile.getInputStream(jarEntry);
}
+
+ @Override
+ public long getSize() {
+ return jarEntry.getSize();
+ }
}
JarIterator(JarFile f) {
@@ -148,4 +206,10 @@ public boolean hasNext() {
public void remove() {
throw new RuntimeException("FileLister does not remove files");
}
+
+ @Override
+ FileLister.Entry open(String relativeFileName) throws IOException {
+ JarEntry e = jarFile.getJarEntry(relativeFileName);
+ return e == null ? null : new JEntry(e);
+ }
}
4 src/kilim/analysis/Frame.java
View
@@ -66,8 +66,8 @@ public Frame merge(Frame inframe, boolean localsOnly, Usage usage) {
Value[] st = stack;
Value[] ist = inframe.stack;
for (int i = 0; i < slen; i++) {
- Value va = ist[i];
- Value vb = st[i];
+ Value va = st[i];
+ Value vb = ist[i];
if (va == vb || va.equals(vb)) continue;
Value newval = va.merge(vb);
if (newval != va) {
5 src/kilim/analysis/MethodFlow.java
View
@@ -23,6 +23,7 @@
import java.util.PriorityQueue;
import kilim.KilimException;
+import kilim.mirrors.Detector;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
@@ -200,9 +201,7 @@ public void visitMethodInsn(int opcode, String owner, String name, String desc)
// functionality.
if (!classFlow.isWoven) {
int methodStatus = detector.getPausableStatus(owner, name, desc);
- if (methodStatus == Detector.METHOD_NOT_FOUND) {
- throw new KilimException("Check classpath. Method " + owner + "." + name + desc + " could not be located");
- } else if (methodStatus == Detector.PAUSABLE_METHOD_FOUND) {
+ if (methodStatus == Detector.PAUSABLE_METHOD_FOUND) {
MethodInsnNode min = (MethodInsnNode)instructions.get(instructions.size()-1);
pausableMethods.add(min);
}
242 src/kilim/analysis/TypeDesc.java
View
@@ -5,6 +5,7 @@
*/
package kilim.analysis;
+
import static kilim.Constants.D_BOOLEAN;
import static kilim.Constants.D_BYTE;
import static kilim.Constants.D_CHAR;
@@ -23,17 +24,18 @@
import kilim.Constants;
import kilim.mirrors.ClassMirrorNotFoundException;
+import kilim.mirrors.Detector;
import org.objectweb.asm.Type;
/**
- * A utility class that provides static methods for interning type strings
- * and merging type descriptors.
- *
+ * A utility class that provides static methods for interning type strings and merging type
+ * descriptors.
+ *
*/
public class TypeDesc {
- static final HashMap<String, String> knownTypes = new HashMap<String,String>(30);
-
+ static final HashMap<String, String> knownTypes = new HashMap<String, String>(30);
+
static {
Field[] fields = Constants.class.getFields();
try {
@@ -50,18 +52,20 @@
knownTypes.put("java/lang/Object", D_OBJECT);
knownTypes.put("java/lang/String", D_STRING);
}
-
+
static boolean isDoubleWord(String desc) {
return (desc == D_DOUBLE || desc == D_LONG);
}
-
+
public static String getInterned(String desc) {
String ret = knownTypes.get(desc);
if (ret == null) {
switch (desc.charAt(0)) {
- case 'L':
- case '[': return desc;
- default: return "L" + desc + ';';
+ case 'L':
+ case '[':
+ return desc;
+ default:
+ return "L" + desc + ';';
}
} else {
return ret;
@@ -69,48 +73,61 @@ public static String getInterned(String desc) {
}
public static String getReturnTypeDesc(String desc) {
- return getInterned(desc.substring(desc.indexOf(")")+1));
+ return getInterned(desc.substring(desc.indexOf(")") + 1));
}
-
+
static boolean isSingleWord(String desc) {
return !isDoubleWord(desc);
}
-
+
public static String getComponentType(String t) {
- if (t.charAt(0) != '[') {
+ if (t.charAt(0) != '[') {
throw new InternalError("Can't get component type of " + t);
}
return getInterned(t.substring(1));
}
+
public static String getTypeDesc(Object object) {
- if (object instanceof Integer) return D_INT;
- if (object instanceof Long) return D_LONG;
- if (object instanceof Float) return D_FLOAT;
- if (object instanceof Double) return D_DOUBLE;
- if (object instanceof String) return D_STRING;
- if (object instanceof Boolean) return D_BOOLEAN;
- if (object instanceof Type)
- return TypeDesc.getInterned(((Type)object).getDescriptor());
+ if (object instanceof Integer)
+ return D_INT;
+ if (object instanceof Long)
+ return D_LONG;
+ if (object instanceof Float)
+ return D_FLOAT;
+ if (object instanceof Double)
+ return D_DOUBLE;
+ if (object instanceof String)
+ return D_STRING;
+ if (object instanceof Boolean)
+ return D_BOOLEAN;
+ if (object instanceof Type)
+ return TypeDesc.getInterned(((Type) object).getDescriptor());
throw new InternalError("Unrecognized ldc constant: " + object);
}
-
+
private static int typelen(char[] buf, int off) {
int start = off;
- switch(buf[off]) {
- case 'L':
- while (buf[off++] != ';') {
- }
- return off - start;
- case 'B': case 'C': case 'D': case 'F': case 'I':
- case 'J': case 'S': case 'Z': case 'V':
- return 1;
- case '[':
- return typelen(buf, off+1) + 1;
- default:
- throw new InternalError("Unknown descriptor type");
+ switch (buf[off]) {
+ case 'L':
+ while (buf[off++] != ';') {}
+ return off - start;
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'I':
+ case 'J':
+ case 'S':
+ case 'Z':
+ case 'V':
+ return 1;
+ case '[':
+ return typelen(buf, off + 1) + 1;
+ default:
+ throw new InternalError("Unknown descriptor type");
}
}
-
+
public static String[] getArgumentTypes(String methodDescriptor) {
char[] buf = methodDescriptor.toCharArray();
int size = getNumArgumentTypes(buf);
@@ -118,23 +135,23 @@ private static int typelen(char[] buf, int off) {
size = 0;
int off = 1;
while (buf[off] != ')') {
- int len = typelen(buf,off);
+ int len = typelen(buf, off);
args[size] = getInterned(new String(buf, off, len));
off += len;
size += 1;
}
return args;
}
-
+
public static int getNumArgumentTypes(String desc) {
return getNumArgumentTypes(desc.toCharArray());
}
-
+
public static int getNumArgumentTypes(char[] buf) {
- int off = 1;
+ int off = 1;
int size = 0;
while (true) {
- if (buf[off]== ')') {
+ if (buf[off] == ')') {
break;
}
off += typelen(buf, off);
@@ -142,92 +159,103 @@ public static int getNumArgumentTypes(char[] buf) {
}
return size;
}
-
+
/**
- * Given two type descriptors, it returns an appropriate merge:
- * 1) If they are Array types, the result is a an array of the
- * merged component types
- * 2) If they are ref types, it returns the least common
- * super type. If one of them is an interface, the result
- * is D_OBJECT
- * 3) All other types must match exactly in order to not
- * raise an error.
+ * Given two type descriptors, it returns an appropriate merge: 1) If they are Array types, the
+ * result is a an array of the merged component types 2) If they are ref types, it returns the
+ * least common super type. If one of them is an interface, the result is D_OBJECT 3) All other
+ * types must match exactly in order to not raise an error.
*/
-
+
public static String mergeType(String a, String b) throws IncompatibleTypesException {
// given: a and b are different.
- if (a == D_UNDEFINED) return b;
- if (b == D_UNDEFINED) return a;
+ if (a == D_UNDEFINED)
+ return b;
+ if (b == D_UNDEFINED)
+ return a;
char ac = a.charAt(0);
char bc = b.charAt(0);
if (a == D_NULL) {
- assert b == D_NULL || bc == 'L' || bc == '[' : "merging NULL type with non ref type: " + b;
+ assert b == D_NULL || bc == 'L' || bc == '[' : "merging NULL type with non ref type: "
+ + b;
return b;
}
if (b == D_NULL) {
- assert b == D_NULL || bc == 'L' || bc == '[' : "merging NULL type with non ref type: " + a;
+ assert b == D_NULL || bc == 'L' || bc == '[' : "merging NULL type with non ref type: "
+ + a;
return a;
}
- if (a == b || a.equals(b)) return a;
+ if (a == b || a.equals(b))
+ return a;
switch (ac) {
- case 'N': // D_NULL
- if (bc == 'L') return b;
- break;
- case 'L':
- if (bc == 'L') {
- return commonSuperType(a,b);
- } else if (bc == 'N') {
- return a;
- } else if (bc == '[') {
- return D_OBJECT; // common supertype of Ref and ArrayRef
- }
- break;
- case '[':
- if (bc == '[') {
- try {
- return "[" + mergeType(TypeDesc.getComponentType(a), TypeDesc.getComponentType(b));
- } catch (IncompatibleTypesException ite) {
- // The component types are incompatible, but two disparate arrays still
- // inherit from Object
- return D_OBJECT;
- }
- } else if (bc == 'L') {
- return D_OBJECT; // common supertype of Ref and ArrayRef
- }
- break;
- case 'I': case 'Z': case 'B': case 'C': case 'S':
- // all int types are interchangeable
- switch (bc) {
- case 'I': case 'Z': case 'B': case 'C': case 'S': return D_INT;
+ case 'N': // D_NULL
+ if (bc == 'L')
+ return b;
+ break;
+ case 'L':
+ if (bc == 'L') {
+ return commonSuperType(a, b);
+ } else if (bc == 'N') {
+ return a;
+ } else if (bc == '[') {
+ return D_OBJECT; // common supertype of Ref and ArrayRef
+ }
+ break;
+ case '[':
+ if (bc == '[') {
+ try {
+ return "["
+ + mergeType(TypeDesc.getComponentType(a), TypeDesc.getComponentType(b));
+ } catch (IncompatibleTypesException ite) {
+ // The component types are incompatible, but two disparate arrays still
+ // inherit from Object
+ return D_OBJECT;
}
- break;
+ } else if (bc == 'L') {
+ return D_OBJECT; // common supertype of Ref and ArrayRef
+ }
+ break;
+ case 'I':
+ case 'Z':
+ case 'B':
+ case 'C':
+ case 'S':
+ // all int types are interchangeable
+ switch (bc) {
+ case 'I':
+ case 'Z':
+ case 'B':
+ case 'C':
+ case 'S':
+ return D_INT;
+ }
+ break;
}
throw new IncompatibleTypesException("" + a + "," + b);
}
-
- static String JAVA_LANG_OBJECT = "java.lang.Object";
-
+
+ static String JAVA_LANG_OBJECT = "java.lang.Object";
+
// public for testing purposes
public static String commonSuperType(String oa, String ob) {
try {
- if (oa == D_OBJECT || ob == D_OBJECT) return D_OBJECT;
- if (oa.equals(ob)) return oa;
-
+ if (oa == D_OBJECT || ob == D_OBJECT)
+ return D_OBJECT;
+ if (oa.equals(ob))
+ return oa;
+
String lub = Detector.getDetector().commonSuperType(oa, ob);
-
- return lub;
-
+
+ return lub;
+
} catch (ClassMirrorNotFoundException cnfe) {
throw new InternalError(cnfe.getMessage());
- }
+ }
}
public static boolean isIntType(String typeDesc) {
- return (typeDesc == D_INT ||
- typeDesc == D_CHAR ||
- typeDesc == D_SHORT ||
- typeDesc == D_BYTE ||
- typeDesc == D_BOOLEAN);
+ return (typeDesc == D_INT || typeDesc == D_CHAR || typeDesc == D_SHORT
+ || typeDesc == D_BYTE || typeDesc == D_BOOLEAN);
}
public static boolean isRefType(String typeDesc) {
@@ -237,15 +265,15 @@ public static boolean isRefType(String typeDesc) {
public static String getInternalName(String desc) {
if (desc.charAt(0) == 'L') {
- return desc.substring(1,desc.length()-1);
+ return desc.substring(1, desc.length() - 1);
} else {
assert desc.charAt(0) == '[' : "Unexpected internal name " + desc;
return desc;
}
}
-
-// public static void main(String[] args) throws Exception {
-// System.out.println(mergeType("Lkilim/test/ex/ExC;", "Lkilim/test/ex/ExD;"));
-// }
-
+
+ // public static void main(String[] args) throws Exception {
+ // System.out.println(mergeType("Lkilim/test/ex/ExC;", "Lkilim/test/ex/ExD;"));
+ // }
+
}
66 src/kilim/analysis/Usage.java
View
@@ -5,16 +5,20 @@
*/
package kilim.analysis;
+
import java.util.ArrayList;
import java.util.BitSet;
/**
- * Each BasicBlock owns one instance of Usage.
- * This class maintains, in essence, three vectors of booleans, indexed by the
- * local variable number. Since it is <i>very</i> rare for a method to
- * have more than 31 local variables, the vectors are represented by int
- * bitmaps. For more than this, the basic block creates an instance of BigUsage
- * that is functionally identical (TODO)
+ * Each BasicBlock owns one instance of Usage. This class maintains, in essence, three vectors of
+ * booleans, indexed by the local variable number. Since it is <i>very</i> rare for a method to have
+ * more than 31 local variables, the vectors are represented by int bitmaps. For more than this, the
+ * basic block creates an instance of BigUsage that is functionally identical (TODO)
+ *
+ * Note that we don't need to track usage of operand stack. All elements of the operand stack are
+ * always live, and always need to be stored and restored (during stack switching). This is not true
+ * of local vars; a var may have a valid value which may not be used downstream, so we track which
+ * vars must be taken seriously.
*
* @see BasicBlock
*/
@@ -30,24 +34,22 @@
private BitSet in;
/**
- * use.bit(i) == 1 (from LSB) if the ith var is read before it has been
- * written. The bit vector as a whole represents the set of vars that the
- * BB needs from its predecessors.
+ * use.bit(i) == 1 (from LSB) if the ith var is read before it has been written. The bit vector
+ * as a whole represents the set of vars that the BB needs from its predecessors.
*/
private BitSet use;
/**
- * def.bit(i) == 1 (from LSB) if the ith var is written into before it has
- * been read. It represents all the vars that this BB is capable of
- * supplying downstream on its own, hence those vars are not required to be
- * supplied by its predecessors (even if they do supply them, they will be
- * overwritten anyway).
+ * def.bit(i) == 1 (from LSB) if the ith var is written into before it has been read. It
+ * represents all the vars that this BB is capable of supplying downstream on its own, hence
+ * those vars are not required to be supplied by its predecessors (even if they do supply them,
+ * they will be overwritten anyway).
*/
private BitSet def;
public Usage(int numLocals) {
nLocals = numLocals;
- in = new BitSet(numLocals);
+ in = new BitSet(numLocals);
use = new BitSet(numLocals);
def = new BitSet(numLocals);
}
@@ -71,24 +73,24 @@ public void write(int var) {
public boolean isLiveIn(int var) {
return in.get(var);
}
-
+
/**
- * This is the standard liveness calculation (Dragon Book, section 10.6). At
- * each BB (and its corresponding usage), we evaluate "in" using use and
- * def. in = use U (out \ def) where out = U succ.in, for all successors
+ * This is the standard liveness calculation (Dragon Book, section 10.6). At each BB (and its
+ * corresponding usage), we evaluate "in" using use and def. in = use U (out \ def) where out =
+ * U succ.in, for all successors
*/
public boolean evalLiveIn(ArrayList<Usage> succUsage) {
BitSet out = new BitSet(nLocals);
- BitSet old_in = (BitSet)in.clone();
+ BitSet old_in = (BitSet) in.clone();
if (succUsage.size() == 0) {
in = use;
} else {
- // calculate out = U succ.in
+ // calculate out = U succ.in
out = (BitSet) succUsage.get(0).in.clone();
for (int i = 1; i < succUsage.size(); i++) {
out.or(succUsage.get(i).in);
}
- // calc out \ def == out & ~def == ~(out | def)
+ // calc out \ def == out & ~def == ~(out | def)
BitSet def1 = (BitSet) def.clone();
def1.flip(0, nLocals);
out.and(def1);
@@ -99,13 +101,12 @@ public boolean evalLiveIn(ArrayList<Usage> succUsage) {
}
/**
- * Called to coalesce a successor's usage into the current BB. Important: This
- * should be called before live variable analysis begins, because we don't
- * bother merging this.in.
+ * Called to coalesce a successor's usage into the current BB. Important: This should be called
+ * before live variable analysis begins, because we don't bother merging this.in.
*/
void absorb(Usage succ) {
- BitSet b = (BitSet)this.def.clone();
- b.flip(0,nLocals);
+ BitSet b = (BitSet) this.def.clone();
+ b.flip(0, nLocals);
b.and(succ.use);
this.use.or(b);
this.def.or(succ.def);
@@ -125,7 +126,8 @@ public String toString() {
private void printBits(StringBuffer sb, BitSet b) {
int numDefined = 0;
for (int i = 0; i < nLocals; i++) {
- if (b.get(i)) numDefined++;
+ if (b.get(i))
+ numDefined++;
}
sb.append('(').append(numDefined).append("): ");
for (int i = 0; i < nLocals; i++) {
@@ -136,13 +138,15 @@ private void printBits(StringBuffer sb, BitSet b) {
}
/**
- * This is purely for testing purposes.
- * @param var local var index
+ * This is purely for testing purposes.
+ *
+ * @param var
+ * local var index
*/
public void setLiveIn(int var) {
in.set(var);
}
-
+
Usage copy() {
Usage ret = new Usage(nLocals);
ret.use = use;
223 src/kilim/mirrors/CachedClassMirrors.java
View
@@ -0,0 +1,223 @@
+package kilim.mirrors;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+
+/**
+ * CachedClassMirrors caches information about a set of classes that are loaded through byte arrays, and which
+ * are not already loaded by the classloader
+ **/
+
+public class CachedClassMirrors extends Mirrors {
+ final static String[] EMPTY_SET = new String[0];
+
+ final RuntimeClassMirrors delegate;
+ ConcurrentHashMap<String,ClassMirror> cachedClasses = new ConcurrentHashMap<String, ClassMirror>();
+
+ public CachedClassMirrors(ClassLoader cl) {
+ delegate = new RuntimeClassMirrors(cl);
+ }
+
+ @Override
+ public ClassMirror classForName(String className)
+ throws ClassMirrorNotFoundException {
+ // defer to loaded class objects first, then to cached class mirrors.
+ ClassMirror ret = cachedClasses.get(className);
+
+ if (ret == null) {
+ ret = delegate.classForName(className);
+ }
+ if (ret == null) {
+ throw new ClassMirrorNotFoundException(className);
+ }
+ return ret;
+ }
+
+ @Override
+ public ClassMirror mirror(Class<?> clazz) {
+ // param is already a class; use the delegate to get the appropriate runtime mirror
+ return delegate.mirror(clazz);
+ }
+
+ @Override
+ public ClassMirror mirror(String className, byte[] bytecode) {
+ // if it is loaded by the classLoader already, we will
+ // not load the classNode, even if the bytes are different
+ ClassMirror ret = null;
+ if (!delegate.isLoaded(className)) {
+ ret = new CachedClassMirror(bytecode);
+ String name = ret.getName().replace('/', '.'); // Class.forName format
+ this.cachedClasses.put(name, ret);
+ }
+ return ret;
+ }
+}
+
+class CachedClassMirror extends ClassMirror implements ClassVisitor {
+
+ String name;
+ boolean isInterface;
+ MethodMirror[] declaredMethods;
+ String[] interfaceNames;
+ String superName;
+
+ private List<CachedMethodMirror> tmpMethodList; //used only while processing bytecode.
+
+ public CachedClassMirror(byte []bytecode) {
+ ClassReader cr = new ClassReader(bytecode);
+ cr.accept(this, true);
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isInterface() {
+ return isInterface;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof CachedClassMirror) {
+ CachedClassMirror mirr = (CachedClassMirror) obj;
+ return mirr.name == this.name && mirr.isInterface == this.isInterface;
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.name.hashCode();
+ }
+
+ @Override
+ public MethodMirror[] getDeclaredMethods() {
+ return declaredMethods;
+ }
+
+ @Override
+ public String[] getInterfaces() throws ClassMirrorNotFoundException {
+ return interfaceNames;
+ }
+
+ @Override
+ public String getSuperclass() throws ClassMirrorNotFoundException {
+ return superName;
+ }
+
+ @Override
+ public boolean isAssignableFrom(ClassMirror c) throws ClassMirrorNotFoundException {
+ Detector d = Detector.getDetector();
+ if (this.equals(c)) return true;
+
+ ClassMirror supcl = d.classForName(c.getSuperclass());
+ if (isAssignableFrom(supcl)) return true;
+ for (String icl: c.getInterfaces()) {
+ supcl = d.classForName(icl);
+ if (isAssignableFrom(supcl))
+ return true;
+ }
+ return false;
+ }
+
+
+ // ClassVisitor implementation
+ @Override
+ public void visit(int version, int access, String name, String signature, String superName,
+ String[] interfaces) {
+ this.name = name;
+ this.superName = superName;
+ this.interfaceNames = interfaces == null ? CachedClassMirrors.EMPTY_SET : interfaces;
+ this.isInterface = (access & Opcodes.ACC_INTERFACE) > 0;
+ }
+
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+ String[] exceptions) {
+ if (tmpMethodList == null) {
+ tmpMethodList = new ArrayList<CachedMethodMirror>();
+ }
+ tmpMethodList.add(new CachedMethodMirror(access, name, desc, exceptions));
+ return null; // null MethodVisitor to avoid examining the instructions.
+ }
+
+ public void visitEnd() {
+ if (tmpMethodList != null) {
+ declaredMethods = new MethodMirror[tmpMethodList.size()];
+ int i = 0;
+ for (MethodMirror mm: tmpMethodList) {
+ declaredMethods[i++] = mm;
+ }
+ tmpMethodList = null;
+ }
+ }
+
+ // Dummy methods
+
+ public void visitSource(String source, String debug) {}
+ public void visitOuterClass(String owner, String name, String desc) {}
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ return DummyAnnotationVisitor.singleton;
+ }
+ public void visitAttribute(Attribute attr) {}
+ public void visitInnerClass(String name, String outerName, String innerName, int access) {}
+ public FieldVisitor visitField(int access, String name, String desc, String signature,
+ Object value) {
+ return null;
+ }
+ static class DummyAnnotationVisitor implements AnnotationVisitor {
+ static DummyAnnotationVisitor singleton = new DummyAnnotationVisitor();
+ public void visit(String name, Object value) {}
+ public AnnotationVisitor visitAnnotation(String name, String desc) {return this;}
+ public AnnotationVisitor visitArray(String name) {return DummyAnnotationVisitor.singleton;}
+ public void visitEnd() {}
+ public void visitEnum(String name, String desc, String value) {}
+ }
+}
+
+class CachedMethodMirror implements MethodMirror {
+
+ private String[] exceptions;
+ private String desc;
+ private String name;
+ private boolean isBridge;
+
+ public CachedMethodMirror(int access, String name, String desc, String[] exceptions) {
+ this.name = name;
+ this.desc = desc;
+ this.exceptions = (exceptions == null) ? CachedClassMirrors.EMPTY_SET : exceptions;
+ isBridge = (access & Opcodes.ACC_BRIDGE) > 0;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String[] getExceptionTypes() throws ClassMirrorNotFoundException {
+ return exceptions;
+ }
+
+ public String getMethodDescriptor() {
+ return desc;
+ }
+
+ public boolean isBridge() {
+ return isBridge;
+ }
+}
+
+
6 src/kilim/mirrors/ClassMirror.java
View
@@ -4,11 +4,11 @@
public abstract MethodMirror[] getDeclaredMethods();
- public abstract boolean isAssignableFrom(ClassMirror c);
+ public abstract boolean isAssignableFrom(ClassMirror c) throws ClassMirrorNotFoundException;
- public abstract ClassMirror getSuperclass();
+ public abstract String getSuperclass() throws ClassMirrorNotFoundException;
- public abstract ClassMirror[] getInterfaces();
+ public abstract String[] getInterfaces() throws ClassMirrorNotFoundException;
public abstract boolean isInterface();
3  src/kilim/mirrors/ClassMirrorNotFoundException.java
View
@@ -7,6 +7,9 @@
*/
private static final long serialVersionUID = 5147833200948234264L;
+ public ClassMirrorNotFoundException (String msg) {
+ super(msg);
+ }
public ClassMirrorNotFoundException(Throwable cause) {
super(cause);
}
233 src/kilim/mirrors/Detector.java
View
@@ -0,0 +1,233 @@
+/* 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.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) && om.getMethodDescriptor().equals(desc)) {
+ 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 D_OBJECT; // This is what the java bytecode verifier does
+ }
+ } catch (ClassMirrorNotFoundException e) {
+ // try to see if the below works...
+ }
+
+ 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);
+ return toDesc(sca.get(lasta + 1));
+ }
+
+ 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) {
+ return s.replace('/', '.').substring(1, s.length() - 1);
+ }
+
+ static String JAVA_LANG_OBJECT = "java.lang.Object";
+
+}
7 src/kilim/mirrors/FieldMirror.java
View
@@ -1,7 +0,0 @@
-package kilim.mirrors;
-
-public interface FieldMirror extends MemberMirror {
-
- public ClassMirror getType();
-
-}
5 src/kilim/mirrors/MemberMirror.java
View
@@ -1,5 +0,0 @@
-package kilim.mirrors;
-
-public interface MemberMirror {
- public abstract String getName();
-}
6 src/kilim/mirrors/MethodMirror.java
View
@@ -2,12 +2,14 @@
-public interface MethodMirror extends MemberMirror {
+public interface MethodMirror {
+
+ public abstract String getName();
/** @see org.objectweb.asm.Type#getMethodDescriptor(java.lang.reflect.Method) */
public abstract String getMethodDescriptor();
- public abstract ClassMirror[] getExceptionTypes() ;
+ public abstract String[] getExceptionTypes() throws ClassMirrorNotFoundException;
public abstract boolean isBridge();
20 src/kilim/mirrors/Mirrors.java
View
@@ -1,22 +1,14 @@
package kilim.mirrors;
-import java.lang.reflect.Field;
-import java.lang.reflect.Member;
-import java.lang.reflect.Method;
-
-
+/**
+ * Mirrors provides a uniform facade for class and method related information
+ * (via ClassMirror and MethodMirror). This information is obtained either through
+ * loaded Class objects or parsed bytecode.
+ */
public abstract class Mirrors {
-
abstract public ClassMirror classForName(String className)
throws ClassMirrorNotFoundException;
public abstract ClassMirror mirror(Class<?> clazz);
- public abstract MethodMirror mirror(Method mth) ;
- public abstract MemberMirror mirror(Member member) ;
- public abstract FieldMirror mirror(Field member) ;
-
- public static Mirrors getRuntimeMirrors() {
- return new RuntimeClassMirrors();
- }
-
+ public abstract ClassMirror mirror(String className, byte[] bytecode);
}
363 src/kilim/mirrors/RuntimeClassMirrors.java
View
@@ -1,187 +1,194 @@
package kilim.mirrors;
-import java.lang.reflect.Field;
-import java.lang.reflect.Member;
import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import kilim.KilimClassLoader;
import org.objectweb.asm.Type;
+/**
+ * This class provides the Mirrors facade over a set of Class objects
+ * @see Mirrors
+ */
+
class RuntimeClassMirrors extends Mirrors {
+ // Weakly cache the mirror objects.
+ Map<String, RuntimeClassMirror> cachedClasses = Collections
+ .synchronizedMap(new WeakHashMap<String, RuntimeClassMirror>());
+
+ public final KilimClassLoader classLoader;
+
+ public RuntimeClassMirrors() {
+ this(Thread.currentThread().getContextClassLoader());
+ }
+
+ public RuntimeClassMirrors(ClassLoader cl) {
+ if (!(cl instanceof KilimClassLoader)) {
+ cl = new KilimClassLoader(cl);
+ }
+ this.classLoader = (KilimClassLoader) cl;
+ }
+
+ @Override
+ public ClassMirror classForName(String className) throws ClassMirrorNotFoundException {
+ try {
+ RuntimeClassMirror ret = cachedClasses.get(className);
+ if (ret == null) {
+ ret = make(classLoader.loadClass(className));
+ }
+ return ret;
+ } catch (ClassNotFoundException e) {
+ throw new ClassMirrorNotFoundException(e);
+ }
+ }
+
+ @Override
+ public ClassMirror mirror(Class<?> clazz) {
+ if (clazz == null)
+ return null;
+ return make(clazz);
+ }
+
+ @Override
+ public ClassMirror mirror(String className, byte[] bytecode) {
+ try {
+ return classForName(className);
+ } catch (ClassMirrorNotFoundException ignore) {}
+ return null;
+ }
+
+ /**
+ * Like classForName, but only if the class is already loaded. This does not force loading of a
+ * class.
+ *
+ * @param className
+ * @return null if className not loaded, else a RuntimeClassMirror to represent the loaded
+ * class.
+ */
+ public ClassMirror loadedClassForName(String className) {
+ Class<?> c = classLoader.getLoadedClass(className);
+ return (c == null) ? null : make(c);
+ }
+
+ public Class<?> getLoadedClass(String className) {
+ return classLoader.getLoadedClass(className);
+ }
+
+ public boolean isLoaded(String className) {
+ return classLoader.isLoaded(className);
+ }
+
+ private RuntimeClassMirror make(Class<?> c) {
+ if (c == null) {
+ throw new NullPointerException();
+ }
+ RuntimeClassMirror ret = new RuntimeClassMirror(c);
+ cachedClasses.put(c.getName(), ret);
+ return ret;
+ }
+}
+
+class RuntimeMethodMirror implements MethodMirror {
+
+ private final Method method;
- static class RuntimeMemberMirror implements MemberMirror {
-
- private final Member member;
-
- public RuntimeMemberMirror(Member member) {
- this.member = member;
- }
-
- public String getName() {
- return member.getName();
- }
-
- }
-
- static class RuntimeFieldMirror extends RuntimeMemberMirror implements FieldMirror {
-
- final private Field field;
-
- public RuntimeFieldMirror(Field field) {
- super(field);
- this.field = field;
- }
-
- public static FieldMirror forField(Field member) {
- if (member == null) return null;
- else return new RuntimeFieldMirror(member);
- }
-
- public ClassMirror getType() {
- return RuntimeClassMirror.forClass(field.getType());
- }
- }
-
- static class RuntimeMethodMirror extends RuntimeMemberMirror implements MethodMirror {
-
- private final Method method;
-
- public RuntimeMethodMirror(Method method) {
- super(method);
- this.method = method;
- }
-
- static MethodMirror forMethod(Method method) {
- if (method==null) return null;
- else return new RuntimeMethodMirror(method);
- }
-
- public static MethodMirror[] forMethods(Method[] declaredMethods) {
- MethodMirror[] result = new MethodMirror[declaredMethods.length];
- for (int i = 0; i < declaredMethods.length; i++) {
- result[i] = RuntimeMethodMirror.forMethod(declaredMethods[i]);
- }
- return result;
- }
-
- public ClassMirror[] getExceptionTypes() {
- return RuntimeClassMirror.forClasses(method.getExceptionTypes());
- }
-
- public String getMethodDescriptor() {
- return Type.getMethodDescriptor(method);
- }
-
- public boolean isBridge() {
- return method.isBridge();
- }
-
- }
-
- static class RuntimeClassMirror extends ClassMirror {
-
- private final Class<?> clazz;
-
- public RuntimeClassMirror(Class<?> clazz) {
- if (clazz == null) throw new NullPointerException();
- this.clazz = clazz;
- }
-
- @Override
- public String getName() {
- return clazz.getName();
- }
-
- @Override
- public boolean isInterface() {
- return clazz.isInterface();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof RuntimeClassMirror) {
- RuntimeClassMirror mirr = (RuntimeClassMirror) obj;
-
- return mirr.clazz == clazz;
- }
-
- return false;
- }
-
- @Override
- public MethodMirror[] getDeclaredMethods() {
- return RuntimeMethodMirror.forMethods(clazz.getDeclaredMethods());
- }
-
- @Override
- public ClassMirror[] getInterfaces() {
- return forClasses(clazz.getInterfaces());
- }
-
- private static ClassMirror forClass(Class<?> clazz) {
- if (clazz==null) return null;
- return new RuntimeClassMirror(clazz);
- }
-
- private static ClassMirror[] forClasses(Class<?>[] classes) {
- ClassMirror[] result = new ClassMirror[classes.length];
- for (int i = 0; i < classes.length; i++) {
- result[i] = RuntimeClassMirror.forClass(classes[i]);
- }
- return result;
- }
-
- @Override
- public ClassMirror getSuperclass() {
- return RuntimeClassMirror.forClass(clazz.getSuperclass());
- }
-
- @Override
- public boolean isAssignableFrom(ClassMirror c) {
- if (c instanceof RuntimeClassMirror) {
- RuntimeClassMirror cc = (RuntimeClassMirror) c;
- return clazz.isAssignableFrom(cc.clazz);
- } else {
- return false;
- }
- }
-
- }
-
- @Override
- public ClassMirror classForName(String className)
- throws ClassMirrorNotFoundException {
- try {
- return new RuntimeClassMirror(Class.forName(className));
- } catch (ClassNotFoundException e) {
- throw new ClassMirrorNotFoundException(e);
- }
- }
-
- @Override
- public ClassMirror mirror(Class<?> clazz) {
- if (clazz == null) return null;
- return new RuntimeClassMirror(clazz);
- }
-
- @Override
- public MethodMirror mirror(Method mth) {
- if (mth == null) return null;
- return RuntimeMethodMirror.forMethod(mth);
- }
-
- @Override
- public FieldMirror mirror(Field member) {
- return RuntimeFieldMirror.forField(member);
- }
-
- @Override
- public MemberMirror mirror(Member member) {
- if (member instanceof Method) {
- return mirror((Method)member);
- } else if (member instanceof Field) {
- return mirror((Field)member);
- } else {
- throw new RuntimeException("member is not field or method?");
- }
- }
+ public RuntimeMethodMirror(Method method) {
+ this.method = method;
+ }
+
+ public String getName() {
+ return method.getName();
+ }
+
+ public String[] getExceptionTypes() {
+ String[] ret = new String[method.getExceptionTypes().length];
+ int i = 0;
+ for (Class<?> excl : method.getExceptionTypes()) {
+ ret[i++] = excl.getName();
+ }
+ return ret;
+ }