package sample.evolve; import javassist.*; import java.io.IOException; /** * Evolution provides a set of methods for instrumenting bytecodes. * * For class evolution, updatable class A is renamed to B. Then an * abstract class named A is produced as the super class of B. If the * original class A has a public method m(), then the abstract class A * has an abstract method m(). * * abstract class A * abstract m() * _makeInstance() * | * class A --------> class B * m() m() * * Also, all the other classes are translated so that "new A(i)" * in the methods is replaced with "_makeInstance(i)". This makes * it possible to change the behavior of the instantiation of * the class A. */ public class Evolution implements Translator { public final static String handlerMethod = "_makeInstance"; public final static String latestVersionField = VersionManager.latestVersionField; public final static String versionManagerMethod = "initialVersion"; private static CtMethod trapMethod; private static final int initialVersion = 0; private ClassPool pool; private String updatableClassName = null; private CtClass updatableClass = null; public void start(ClassPool _pool) throws NotFoundException { pool = _pool; // Get the definition of Sample.make() and store it into trapMethod // for later use. trapMethod = _pool.getMethod("sample.evolve.Sample", "make"); } public void onWrite(ClassPool _pool, CtClass clazz) throws NotFoundException, CannotCompileException { onWriteUpdatable(clazz.getName()); /* * Replaces all the occurrences of the new operator with a call * to _makeInstance(). */ CtClass absClass = updatableClass; CodeConverter converter = new CodeConverter(); converter.replaceNew(absClass, absClass, handlerMethod); clazz.instrument(converter); } private void onWriteUpdatable(String classname) throws NotFoundException, CannotCompileException { // if the class is a concrete class, // classname is $. int i = classname.lastIndexOf('$'); if (i <= 0) return; String orgname = classname.substring(0, i); if (!orgname.equals(updatableClassName)) return; int version; try { version = Integer.parseInt(classname.substring(i + 1)); } catch (NumberFormatException e) { throw new NotFoundException(classname, e); } CtClass clazz = pool.getAndRename(orgname, classname); makeConcreteClass(clazz, updatableClass, version); } /* Register an updatable class. */ public void makeUpdatable(String classname) throws NotFoundException, CannotCompileException { if (pool == null) throw new RuntimeException( "Evolution has not been linked to ClassPool."); CtClass c = pool.get(classname); updatableClassName = classname; updatableClass = makeAbstractClass(c); } /** * Produces an abstract class. */ protected CtClass makeAbstractClass(CtClass clazz) throws CannotCompileException, NotFoundException { int i; CtClass absClass = pool.makeClass(clazz.getName()); absClass.setModifiers(Modifier.PUBLIC | Modifier.ABSTRACT); absClass.setSuperclass(clazz.getSuperclass()); absClass.setInterfaces(clazz.getInterfaces()); // absClass.inheritAllConstructors(); CtField fld = new CtField(pool.get("java.lang.Class"), latestVersionField, absClass); fld.setModifiers(Modifier.PUBLIC | Modifier.STATIC); CtField.Initializer finit = CtField.Initializer.byCall( pool.get("sample.evolve.VersionManager"), versionManagerMethod, new String[] { clazz.getName() }); absClass.addField(fld, finit); CtField[] fs = clazz.getDeclaredFields(); for (i = 0; i < fs.length; ++i) { CtField f = fs[i]; if (Modifier.isPublic(f.getModifiers())) absClass.addField(new CtField(f.getType(), f.getName(), absClass)); } CtConstructor[] cs = clazz.getDeclaredConstructors(); for (i = 0; i < cs.length; ++i) { CtConstructor c = cs[i]; int mod = c.getModifiers(); if (Modifier.isPublic(mod)) { CtMethod wm = CtNewMethod.wrapped(absClass, handlerMethod, c.getParameterTypes(), c.getExceptionTypes(), trapMethod, null, absClass); wm.setModifiers(Modifier.PUBLIC | Modifier.STATIC); absClass.addMethod(wm); } } CtMethod[] ms = clazz.getDeclaredMethods(); for (i = 0; i < ms.length; ++i) { CtMethod m = ms[i]; int mod = m.getModifiers(); if (Modifier.isPublic(mod)) if (Modifier.isStatic(mod)) throw new CannotCompileException( "static methods are not supported."); else { CtMethod m2 = CtNewMethod.abstractMethod(m.getReturnType(), m.getName(), m.getParameterTypes(), m.getExceptionTypes(), absClass); absClass.addMethod(m2); } } return absClass; } /** * Modifies the given class file so that it is a subclass of the * abstract class produced by makeAbstractClass(). * * Note: the naming convention must be consistent with * VersionManager.update(). */ protected void makeConcreteClass(CtClass clazz, CtClass abstractClass, int version) throws CannotCompileException, NotFoundException { int i; clazz.setSuperclass(abstractClass); CodeConverter converter = new CodeConverter(); CtField[] fs = clazz.getDeclaredFields(); for (i = 0; i < fs.length; ++i) { CtField f = fs[i]; if (Modifier.isPublic(f.getModifiers())) converter.redirectFieldAccess(f, abstractClass, f.getName()); } CtConstructor[] cs = clazz.getDeclaredConstructors(); for (i = 0; i < cs.length; ++i) cs[i].instrument(converter); CtMethod[] ms = clazz.getDeclaredMethods(); for (i = 0; i < ms.length; ++i) ms[i].instrument(converter); } }