169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalpackage sample.evolve; 269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalimport javassist.*; 469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal/** 669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Evolution provides a set of methods for instrumenting bytecodes. 769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * 869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * For class evolution, updatable class A is renamed to B. Then an abstract 969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * class named A is produced as the super class of B. If the original class A 1069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * has a public method m(), then the abstract class A has an abstract method 1169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * m(). 1269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * 1369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * abstract class A abstract m() _makeInstance() | class A --------> class B m() 1469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * m() 1569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * 1669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Also, all the other classes are translated so that "new A(i)" in the methods 1769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * is replaced with "_makeInstance(i)". This makes it possible to change the 1869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * behavior of the instantiation of the class A. 1969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal */ 2069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigalpublic class Evolution implements Translator { 2169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal public final static String handlerMethod = "_makeInstance"; 2269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 2369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal public final static String latestVersionField = VersionManager.latestVersionField; 2469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 2569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal public final static String versionManagerMethod = "initialVersion"; 2669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 2769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal private static CtMethod trapMethod; 2869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 2969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal private static final int initialVersion = 0; 3069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 3169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal private ClassPool pool; 3269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 3369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal private String updatableClassName = null; 3469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 3569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal private CtClass updatableClass = null; 3669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 3769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal public void start(ClassPool _pool) throws NotFoundException { 3869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal pool = _pool; 3969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 4069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal // Get the definition of Sample.make() and store it into trapMethod 4169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal // for later use. 4269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal trapMethod = _pool.getMethod("sample.evolve.Sample", "make"); 4369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal } 4469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 4569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal public void onLoad(ClassPool _pool, String classname) 4669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal throws NotFoundException, CannotCompileException { 4769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal onLoadUpdatable(classname); 4869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 4969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal /* 5069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Replaces all the occurrences of the new operator with a call to 5169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * _makeInstance(). 5269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal */ 5369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CtClass clazz = _pool.get(classname); 5469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CtClass absClass = updatableClass; 5569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CodeConverter converter = new CodeConverter(); 5669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal converter.replaceNew(absClass, absClass, handlerMethod); 5769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal clazz.instrument(converter); 5869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal } 5969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 6069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal private void onLoadUpdatable(String classname) throws NotFoundException, 6169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CannotCompileException { 6269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal // if the class is a concrete class, 6369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal // classname is <updatableClassName>$$<version>. 6469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 6569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal int i = classname.lastIndexOf("$$"); 6669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal if (i <= 0) 6769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal return; 6869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 6969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal String orgname = classname.substring(0, i); 7069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal if (!orgname.equals(updatableClassName)) 7169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal return; 7269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 7369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal int version; 7469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal try { 7569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal version = Integer.parseInt(classname.substring(i + 2)); 7669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal } 7769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal catch (NumberFormatException e) { 7869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal throw new NotFoundException(classname, e); 7969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal } 8069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 8169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CtClass clazz = pool.getAndRename(orgname, classname); 8269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal makeConcreteClass(clazz, updatableClass, version); 8369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal } 8469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 8569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal /* 8669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Register an updatable class. 8769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal */ 8869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal public void makeUpdatable(String classname) throws NotFoundException, 8969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CannotCompileException { 9069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal if (pool == null) 9169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal throw new RuntimeException( 9269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal "Evolution has not been linked to ClassPool."); 9369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 9469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CtClass c = pool.get(classname); 9569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal updatableClassName = classname; 9669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal updatableClass = makeAbstractClass(c); 9769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal } 9869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 9969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal /** 10069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Produces an abstract class. 10169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal */ 10269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal protected CtClass makeAbstractClass(CtClass clazz) 10369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal throws CannotCompileException, NotFoundException { 10469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal int i; 10569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 10669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CtClass absClass = pool.makeClass(clazz.getName()); 10769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal absClass.setModifiers(Modifier.PUBLIC | Modifier.ABSTRACT); 10869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal absClass.setSuperclass(clazz.getSuperclass()); 10969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal absClass.setInterfaces(clazz.getInterfaces()); 11069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 11169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal // absClass.inheritAllConstructors(); 11269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 11369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CtField fld = new CtField(pool.get("java.lang.Class"), 11469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal latestVersionField, absClass); 11569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal fld.setModifiers(Modifier.PUBLIC | Modifier.STATIC); 11669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 11769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CtField.Initializer finit = CtField.Initializer.byCall(pool 11869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal .get("sample.evolve.VersionManager"), versionManagerMethod, 11969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal new String[] { clazz.getName() }); 12069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal absClass.addField(fld, finit); 12169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 12269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CtField[] fs = clazz.getDeclaredFields(); 12369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal for (i = 0; i < fs.length; ++i) { 12469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CtField f = fs[i]; 12569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal if (Modifier.isPublic(f.getModifiers())) 12669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal absClass.addField(new CtField(f.getType(), f.getName(), 12769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal absClass)); 12869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal } 12969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 13069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CtConstructor[] cs = clazz.getDeclaredConstructors(); 13169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal for (i = 0; i < cs.length; ++i) { 13269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CtConstructor c = cs[i]; 13369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal int mod = c.getModifiers(); 13469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal if (Modifier.isPublic(mod)) { 13569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CtMethod wm = CtNewMethod.wrapped(absClass, handlerMethod, c 13669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal .getParameterTypes(), c.getExceptionTypes(), 13769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal trapMethod, null, absClass); 13869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal wm.setModifiers(Modifier.PUBLIC | Modifier.STATIC); 13969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal absClass.addMethod(wm); 14069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal } 14169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal } 14269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 14369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CtMethod[] ms = clazz.getDeclaredMethods(); 14469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal for (i = 0; i < ms.length; ++i) { 14569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CtMethod m = ms[i]; 14669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal int mod = m.getModifiers(); 14769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal if (Modifier.isPublic(mod)) 14869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal if (Modifier.isStatic(mod)) 14969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal throw new CannotCompileException( 15069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal "static methods are not supported."); 15169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal else { 15269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CtMethod m2 = CtNewMethod.abstractMethod(m.getReturnType(), 15369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal m.getName(), m.getParameterTypes(), m 15469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal .getExceptionTypes(), absClass); 15569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal absClass.addMethod(m2); 15669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal } 15769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal } 15869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 15969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal return absClass; 16069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal } 16169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 16269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal /** 16369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Modifies the given class file so that it is a subclass of the abstract 16469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * class produced by makeAbstractClass(). 16569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * 16669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * Note: the naming convention must be consistent with 16769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal * VersionManager.update(). 16869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal */ 16969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal protected void makeConcreteClass(CtClass clazz, CtClass abstractClass, 17069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal int version) throws CannotCompileException, NotFoundException { 17169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal int i; 17269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal clazz.setSuperclass(abstractClass); 17369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CodeConverter converter = new CodeConverter(); 17469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CtField[] fs = clazz.getDeclaredFields(); 17569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal for (i = 0; i < fs.length; ++i) { 17669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CtField f = fs[i]; 17769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal if (Modifier.isPublic(f.getModifiers())) 17869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal converter.redirectFieldAccess(f, abstractClass, f.getName()); 17969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal } 18069e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 18169e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CtConstructor[] cs = clazz.getDeclaredConstructors(); 18269e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal for (i = 0; i < cs.length; ++i) 18369e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal cs[i].instrument(converter); 18469e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal 18569e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal CtMethod[] ms = clazz.getDeclaredMethods(); 18669e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal for (i = 0; i < ms.length; ++i) 18769e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal ms[i].instrument(converter); 18869e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal } 18969e17611504376e4d4603925f8528dfc890fd2c6Luis Sigal} 190