• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
start(ClassPool _pool)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 
onLoad(ClassPool _pool, String classname)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 
onLoadUpdatable(String classname)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      */
makeUpdatable(String classname)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      */
makeAbstractClass(CtClass clazz)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      */
makeConcreteClass(CtClass clazz, CtClass abstractClass, int version)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