/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