Permalink
Browse files

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.
  • Loading branch information...
1 parent 247f18c commit c95623bd8e487c1b08a588824e1ec696af84d7f5 @kilim committed May 7, 2011
@@ -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;
+ }
+}
@@ -0,0 +1,103 @@
+package kilim;
+
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import kilim.analysis.ClassInfo;
+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<File> paths;
+ /**
+ * 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(":");
+ paths = new ArrayList<File>(classPaths.length);
+ for (String name : classPaths) {
+ name = name.trim();
+ if (name.equals(""))
+ continue;
+ File f = new File(name.trim());
+ if (f.exists() && (f.isDirectory() || name.endsWith(".jar"))) {
+ paths.add(f);
+ }
+ // else {
+ // 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 (File path : paths) {
+ try {
+ File f = new File(path.getAbsolutePath() + File.separatorChar
+ + name.replace('.', File.separatorChar) + ".class");
+ System.err.println("Searching for " + f.getAbsolutePath());
+ if (!f.exists())
+ continue;
+ byte[] code = readFile(f);
+ 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[] readFile(File f) throws IOException {
+ DataInputStream in = new DataInputStream(new FileInputStream(f));
+ byte[] contents = new byte[in.available()];
+ in.readFully(contents);
+ in.close();
+ return contents;
+ }
+}
@@ -20,7 +20,7 @@
public class CachedClassMirrors extends Mirrors {
- final Mirrors delegate;
+ final RuntimeClassMirrors delegate;
ConcurrentHashMap<String,ClassMirror> cachedClasses = new ConcurrentHashMap<String, ClassMirror>();
public CachedClassMirrors(ClassLoader cl) {
@@ -31,12 +31,10 @@ public CachedClassMirrors(ClassLoader cl) {
public ClassMirror classForName(String className)
throws ClassMirrorNotFoundException {
// defer to loaded class objects first, then to cached class mirrors.
- ClassMirror ret = null;
- try {
- ret = delegate.classForName(className);
- } catch (ClassMirrorNotFoundException ignore) {}
+ ClassMirror ret = cachedClasses.get(className);
+
if (ret == null) {
- ret = cachedClasses.get(className);
+ ret = delegate.classForName(className);
}
if (ret == null) {
throw new ClassMirrorNotFoundException(className);
@@ -55,9 +53,7 @@ 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;
- try {
- ret = delegate.classForName(className);
- } catch (ClassMirrorNotFoundException ignore) {
+ if (!delegate.isLoaded(className)) {
ret = new CachedClassMirror(bytecode);
String name = ret.getName().replace('/', '.'); // Class.forName format
this.cachedClasses.put(name, ret);
@@ -26,7 +26,7 @@
// 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());
+ public static final Detector DEFAULT = new Detector(new RuntimeClassMirrors());
public final Mirrors mirrors;
@@ -1,15 +1,14 @@
package kilim.mirrors;
+/**
+ * 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 ClassMirror mirror(String className, byte[] bytecode);
-
- public static Mirrors getRuntimeMirrors() {
- return new RuntimeClassMirrors();
- }
-
}
@@ -1,24 +1,45 @@
package kilim.mirrors;
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 {
- ClassLoader classLoader;
+ // 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) {
- this.classLoader = cl;
+ if (!(cl instanceof KilimClassLoader)) {
+ cl = new KilimClassLoader(cl);
+ }
+ this.classLoader = (KilimClassLoader) cl;
}
@Override
public ClassMirror classForName(String className) throws ClassMirrorNotFoundException {
try {
- return new RuntimeClassMirror(classLoader.loadClass(className));
+ RuntimeClassMirror ret = cachedClasses.get(className);
+ if (ret == null) {
+ ret = make(classLoader.loadClass(className));
+ }
+ return ret;
} catch (ClassNotFoundException e) {
throw new ClassMirrorNotFoundException(e);
}
@@ -28,7 +49,7 @@ public ClassMirror classForName(String className) throws ClassMirrorNotFoundExce
public ClassMirror mirror(Class<?> clazz) {
if (clazz == null)
return null;
- return new RuntimeClassMirror(clazz);
+ return make(clazz);
}
@Override
@@ -38,6 +59,36 @@ public ClassMirror mirror(String className, byte[] bytecode) {
} 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 {
@@ -48,24 +99,21 @@ public RuntimeMethodMirror(Method method) {
this.method = method;
}
- public static MethodMirror[] forMethods(Method[] declaredMethods) {
- MethodMirror[] result = new MethodMirror[declaredMethods.length];
- for (int i = 0; i < declaredMethods.length; i++) {
- result[i] = new RuntimeMethodMirror(declaredMethods[i]);
- }
- return result;
- }
-
public String getName() {
return method.getName();
}
public ClassMirror[] getExceptionTypes() {
+ Detector d = Detector.getDetector();
ClassMirror[] ret = new ClassMirror[method.getExceptionTypes().length];
int i = 0;
- for (Class<?> excl : method.getExceptionTypes()) {
- ret[i++] = new RuntimeClassMirror(excl);
- }
+ try {
+ for (Class<?> excl : method.getExceptionTypes()) {
+ ret[i++] = d.classForName(excl.getName());
+ }
+ } catch (ClassMirrorNotFoundException ignore) {
+ // This exception will not be thrown because the classes are already present
+ }
return ret;
}
@@ -81,7 +129,8 @@ public boolean isBridge() {
class RuntimeClassMirror extends ClassMirror {
private final Class<?> clazz;
-
+ private MethodMirror[] methods;
+
public RuntimeClassMirror(Class<?> clazz) {
this.clazz = clazz;
}
@@ -99,42 +148,52 @@ public boolean isInterface() {
@Override
public boolean equals(Object obj) {
if (obj instanceof ClassMirror) {
- return ((ClassMirror)obj).getName().equals(this.getName());
+ return ((ClassMirror) obj).getName().equals(this.getName());
}
return false;
}
-
+
@Override
public int hashCode() {
return clazz.hashCode();
}
@Override
public MethodMirror[] getDeclaredMethods() {
- return RuntimeMethodMirror.forMethods(clazz.getDeclaredMethods());
+ if (methods == null) {
+ Method[] declaredMethods = clazz.getDeclaredMethods();
+ methods = new MethodMirror[declaredMethods.length];
+ for (int i = 0; i < declaredMethods.length; i++) {
+ methods[i] = new RuntimeMethodMirror(declaredMethods[i]);
+ }
+ }
+ return methods;
}
@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) {
+ Detector d = Detector.getDetector();
ClassMirror[] result = new ClassMirror[classes.length];
for (int i = 0; i < classes.length; i++) {
- result[i] = RuntimeClassMirror.forClass(classes[i]);
+ try {
+ result[i] = d.classForName(classes[i].getName());
+ } catch (ClassMirrorNotFoundException ignore) {
+ // This exception will never be thrown because the Class objects exist
+ }
}
return result;
}
@Override
public ClassMirror getSuperclass() {
- return forClass(clazz.getSuperclass());
+ try {
+ return Detector.getDetector().classForName(clazz.getSuperclass().getName());
+ } catch (ClassMirrorNotFoundException ignore) {} // will never get thrown
+ return null;
}
@Override
Oops, something went wrong.

0 comments on commit c95623b

Please sign in to comment.