1 package sample.evolve; 2 3 import javassist.*; 4 5 /** 6 * Evolution provides a set of methods for instrumenting bytecodes. 7 * 8 * For class evolution, updatable class A is renamed to B. Then an abstract 9 * class named A is produced as the super class of B. If the original class A 10 * has a public method m(), then the abstract class A has an abstract method 11 * m(). 12 * 13 * abstract class A abstract m() _makeInstance() | class A --------> class B m() 14 * m() 15 * 16 * Also, all the other classes are translated so that "new A(i)" in the methods 17 * is replaced with "_makeInstance(i)". This makes it possible to change the 18 * behavior of the instantiation of the class A. 19 */ 20 public class Evolution implements Translator { 21 public final static String handlerMethod = "_makeInstance"; 22 23 public final static String latestVersionField = VersionManager.latestVersionField; 24 25 public final static String versionManagerMethod = "initialVersion"; 26 27 private static CtMethod trapMethod; 28 29 private static final int initialVersion = 0; 30 31 private ClassPool pool; 32 33 private String updatableClassName = null; 34 35 private CtClass updatableClass = null; 36 37 public void start(ClassPool _pool) throws NotFoundException { 38 pool = _pool; 39 40 // Get the definition of Sample.make() and store it into trapMethod 41 // for later use. 42 trapMethod = _pool.getMethod("sample.evolve.Sample", "make"); 43 } 44 45 public void onLoad(ClassPool _pool, String classname) 46 throws NotFoundException, CannotCompileException { 47 onLoadUpdatable(classname); 48 49 /* 50 * Replaces all the occurrences of the new operator with a call to 51 * _makeInstance(). 52 */ 53 CtClass clazz = _pool.get(classname); 54 CtClass absClass = updatableClass; 55 CodeConverter converter = new CodeConverter(); 56 converter.replaceNew(absClass, absClass, handlerMethod); 57 clazz.instrument(converter); 58 } 59 60 private void onLoadUpdatable(String classname) throws NotFoundException, 61 CannotCompileException { 62 // if the class is a concrete class, 63 // classname is <updatableClassName>$$<version>. 64 65 int i = classname.lastIndexOf("$$"); 66 if (i <= 0) 67 return; 68 69 String orgname = classname.substring(0, i); 70 if (!orgname.equals(updatableClassName)) 71 return; 72 73 int version; 74 try { 75 version = Integer.parseInt(classname.substring(i + 2)); 76 } 77 catch (NumberFormatException e) { 78 throw new NotFoundException(classname, e); 79 } 80 81 CtClass clazz = pool.getAndRename(orgname, classname); 82 makeConcreteClass(clazz, updatableClass, version); 83 } 84 85 /* 86 * Register an updatable class. 87 */ 88 public void makeUpdatable(String classname) throws NotFoundException, 89 CannotCompileException { 90 if (pool == null) 91 throw new RuntimeException( 92 "Evolution has not been linked to ClassPool."); 93 94 CtClass c = pool.get(classname); 95 updatableClassName = classname; 96 updatableClass = makeAbstractClass(c); 97 } 98 99 /** 100 * Produces an abstract class. 101 */ 102 protected CtClass makeAbstractClass(CtClass clazz) 103 throws CannotCompileException, NotFoundException { 104 int i; 105 106 CtClass absClass = pool.makeClass(clazz.getName()); 107 absClass.setModifiers(Modifier.PUBLIC | Modifier.ABSTRACT); 108 absClass.setSuperclass(clazz.getSuperclass()); 109 absClass.setInterfaces(clazz.getInterfaces()); 110 111 // absClass.inheritAllConstructors(); 112 113 CtField fld = new CtField(pool.get("java.lang.Class"), 114 latestVersionField, absClass); 115 fld.setModifiers(Modifier.PUBLIC | Modifier.STATIC); 116 117 CtField.Initializer finit = CtField.Initializer.byCall(pool 118 .get("sample.evolve.VersionManager"), versionManagerMethod, 119 new String[] { clazz.getName() }); 120 absClass.addField(fld, finit); 121 122 CtField[] fs = clazz.getDeclaredFields(); 123 for (i = 0; i < fs.length; ++i) { 124 CtField f = fs[i]; 125 if (Modifier.isPublic(f.getModifiers())) 126 absClass.addField(new CtField(f.getType(), f.getName(), 127 absClass)); 128 } 129 130 CtConstructor[] cs = clazz.getDeclaredConstructors(); 131 for (i = 0; i < cs.length; ++i) { 132 CtConstructor c = cs[i]; 133 int mod = c.getModifiers(); 134 if (Modifier.isPublic(mod)) { 135 CtMethod wm = CtNewMethod.wrapped(absClass, handlerMethod, c 136 .getParameterTypes(), c.getExceptionTypes(), 137 trapMethod, null, absClass); 138 wm.setModifiers(Modifier.PUBLIC | Modifier.STATIC); 139 absClass.addMethod(wm); 140 } 141 } 142 143 CtMethod[] ms = clazz.getDeclaredMethods(); 144 for (i = 0; i < ms.length; ++i) { 145 CtMethod m = ms[i]; 146 int mod = m.getModifiers(); 147 if (Modifier.isPublic(mod)) 148 if (Modifier.isStatic(mod)) 149 throw new CannotCompileException( 150 "static methods are not supported."); 151 else { 152 CtMethod m2 = CtNewMethod.abstractMethod(m.getReturnType(), 153 m.getName(), m.getParameterTypes(), m 154 .getExceptionTypes(), absClass); 155 absClass.addMethod(m2); 156 } 157 } 158 159 return absClass; 160 } 161 162 /** 163 * Modifies the given class file so that it is a subclass of the abstract 164 * class produced by makeAbstractClass(). 165 * 166 * Note: the naming convention must be consistent with 167 * VersionManager.update(). 168 */ 169 protected void makeConcreteClass(CtClass clazz, CtClass abstractClass, 170 int version) throws CannotCompileException, NotFoundException { 171 int i; 172 clazz.setSuperclass(abstractClass); 173 CodeConverter converter = new CodeConverter(); 174 CtField[] fs = clazz.getDeclaredFields(); 175 for (i = 0; i < fs.length; ++i) { 176 CtField f = fs[i]; 177 if (Modifier.isPublic(f.getModifiers())) 178 converter.redirectFieldAccess(f, abstractClass, f.getName()); 179 } 180 181 CtConstructor[] cs = clazz.getDeclaredConstructors(); 182 for (i = 0; i < cs.length; ++i) 183 cs[i].instrument(converter); 184 185 CtMethod[] ms = clazz.getDeclaredMethods(); 186 for (i = 0; i < ms.length; ++i) 187 ms[i].instrument(converter); 188 } 189 } 190