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