Home | History | Annotate | Download | only in evolve
      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