/Users/lyon/j4p/src/javassist/reflect/Reflection.java

1    /* 
2     * Javassist, a Java-bytecode translator toolkit. 
3     * Copyright (C) 1999-2003 Shigeru Chiba. All Rights Reserved. 
4     * 
5     * The contents of this file are subject to the Mozilla Public License Version 
6     * 1.1 (the "License"); you may not use this file except in compliance with 
7     * the License.  Alternatively, the contents of this file may be used under 
8     * the terms of the GNU Lesser General Public License Version 2.1 or later. 
9     * 
10    * Software distributed under the License is distributed on an "AS IS" basis, 
11    * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
12    * for the specific language governing rights and limitations under the 
13    * License. 
14    */ 
15    
16   package javassist.reflect; 
17    
18   import javassist.*; 
19    
20   import java.io.IOException; 
21    
22   import javassist.CtMethod.ConstParameter; 
23    
24   /** 
25    * The class implementing the reflection mechanism. 
26    * 
27    * <p>This class is used with <code>ClassPool</code>. 
28    * Note that it implements an interface <code>javassist.Translator</code>. 
29    * 
30    * <p>If a class is reflective, 
31    * then all the method invocations on every 
32    * instance of that class are intercepted by the runtime 
33    * metaobject controlling that instance. 
34    * To do this, the original class file representing a reflective class: 
35    * 
36    * <ul><pre> 
37    * class Person { 
38    *   public int f(int i) { return i + 1; } 
39    *   public int value; 
40    * } 
41    * </pre></ul> 
42    * 
43    * <p>is modified so that it represents a class: 
44    * 
45    * <ul><pre> 
46    * class Person implements Metalevel { 
47    *   public int _original_f(int i) { return i + 1; } 
48    *   public int f(int i) { <i>delegate to the metaobject</i> } 
49    * 
50    *   public int value; 
51    *   public int _r_value() { <i>read "value"</i> } 
52    *   public void _w_value(int v) { <i>write "value"</i> } 
53    * 
54    *   public ClassMetaobject _getClass() { <i>return a class metaobject</i> } 
55    *   public Metaobject _getMetaobject() { <i>return a metaobject</i> } 
56    *   public void _setMetaobject(Metaobject m) { <i>change a metaobject</i> } 
57    * } 
58    * </pre></ul> 
59    * 
60    * @see javassist.reflect.ClassMetaobject 
61    * @see javassist.reflect.Metaobject 
62    * @see javassist.reflect.Loader 
63    * @see javassist.reflect.Compiler 
64    * @see javassist.ClassPool 
65    * @see javassist.Translator 
66    */ 
67   public class Reflection implements Translator { 
68    
69       static final String classobjectField = "_classobject"; 
70       static final String classobjectAccessor = "_getClass"; 
71       static final String metaobjectField = "_metaobject"; 
72       static final String metaobjectGetter = "_getMetaobject"; 
73       static final String metaobjectSetter = "_setMetaobject"; 
74       static final String readPrefix = "_r_"; 
75       static final String writePrefix = "_w_"; 
76    
77       protected CtMethod trapMethod, trapStaticMethod; 
78       protected CtMethod trapRead, trapWrite; 
79       protected CtClass[] readParam; 
80    
81       protected ClassPool classPool; 
82       protected CodeConverter converter; 
83    
84       private boolean isExcluded(String name) { 
85           return name.startsWith(ClassMetaobject.methodPrefix) 
86                   || name.equals(classobjectAccessor) 
87                   || name.equals(metaobjectSetter) 
88                   || name.equals(metaobjectGetter) 
89                   || name.startsWith(readPrefix) 
90                   || name.startsWith(writePrefix); 
91       } 
92    
93       /** 
94        * Constructs a new <code>Reflection</code> object. 
95        */ 
96       public Reflection() { 
97           classPool = null; 
98           converter = new CodeConverter(); 
99       } 
100   
101      /** 
102       * Initializes. 
103       */ 
104      public void start(ClassPool pool) throws NotFoundException { 
105          classPool = pool; 
106          final String msg 
107                  = "javassist.reflect.Sample is not found or broken."; 
108          try { 
109              CtClass c = classPool.get("javassist.reflect.Sample"); 
110              trapMethod = c.getDeclaredMethod("trap"); 
111              trapStaticMethod = c.getDeclaredMethod("trapStatic"); 
112              trapRead = c.getDeclaredMethod("trapRead"); 
113              trapWrite = c.getDeclaredMethod("trapWrite"); 
114              readParam 
115                      = new CtClass[]{classPool.get("java.lang.Object")}; 
116          } catch (NotFoundException e) { 
117              throw new RuntimeException(msg); 
118          } 
119      } 
120   
121      /** 
122       * Inserts hooks for intercepting accesses to the fields declared 
123       * in reflective classes. 
124       */ 
125      public void onWrite(ClassPool pool, String classname) 
126              throws CannotCompileException, NotFoundException { 
127          CtClass c = pool.get(classname); 
128          c.instrument(converter); 
129      } 
130   
131      /** 
132       * Produces a reflective class. 
133       * If the super class is also made reflective, it must be done 
134       * before the sub class. 
135       * 
136       * @param classname         the name of the reflective class 
137       * @param metaobject        the class name of metaobjects. 
138       * @param metaclass         the class name of the class metaobject. 
139       * @return <code>false</code>       if the class is already reflective. 
140       * 
141       * @see javassist.reflect.Metaobject 
142       * @see javassist.reflect.ClassMetaobject 
143       */ 
144      public boolean makeReflective(String classname, 
145                                    String metaobject, String metaclass) 
146              throws CannotCompileException, NotFoundException { 
147          return makeReflective(classPool.get(classname), 
148                  classPool.get(metaobject), 
149                  classPool.get(metaclass)); 
150      } 
151   
152      /** 
153       * Produces a reflective class. 
154       * If the super class is also made reflective, it must be done 
155       * before the sub class. 
156       * 
157       * @param clazz             the reflective class. 
158       * @param metaobject        the class of metaobjects. 
159       *                          It must be a subclass of 
160       *                          <code>Metaobject</code>. 
161       * @param metaclass         the class of the class metaobject. 
162       *                          It must be a subclass of 
163       *                          <code>ClassMetaobject</code>. 
164       * @return <code>false</code>       if the class is already reflective. 
165       * 
166       * @see javassist.reflect.Metaobject 
167       * @see javassist.reflect.ClassMetaobject 
168       */ 
169      public boolean makeReflective(Class clazz, 
170                                    Class metaobject, Class metaclass) 
171              throws CannotCompileException, NotFoundException { 
172          return makeReflective(clazz.getName(), metaobject.getName(), 
173                  metaclass.getName()); 
174      } 
175   
176      /** 
177       * Produces a reflective class.  It modifies the given 
178       * <code>CtClass</code> object and makes it reflective. 
179       * If the super class is also made reflective, it must be done 
180       * before the sub class. 
181       * 
182       * @param clazz             the reflective class. 
183       * @param metaobject        the class of metaobjects. 
184       *                          It must be a subclass of 
185       *                          <code>Metaobject</code>. 
186       * @param metaclass         the class of the class metaobject. 
187       *                          It must be a subclass of 
188       *                          <code>ClassMetaobject</code>. 
189       * @return <code>false</code>       if the class is already reflective. 
190       * 
191       * @see javassist.reflect.Metaobject 
192       * @see javassist.reflect.ClassMetaobject 
193       */ 
194      public boolean makeReflective(CtClass clazz, 
195                                    CtClass metaobject, CtClass metaclass) 
196              throws CannotCompileException, NotFoundException { 
197          registerReflectiveClass(clazz); 
198          return modifyClassfile(clazz, metaobject, metaclass); 
199      } 
200   
201      /** 
202       * Registers a reflective class.  The field accesses to the instances 
203       * of this class are instrumented. 
204       */ 
205      private void registerReflectiveClass(CtClass clazz) { 
206          CtField[] fs = clazz.getDeclaredFields(); 
207          for (int i = 0; i < fs.length; ++i) { 
208              CtField f = fs[i]; 
209              int mod = f.getModifiers(); 
210              if ((mod & Modifier.PUBLIC) != 0 && (mod & Modifier.FINAL) == 0) { 
211                  String name = f.getName(); 
212                  converter.replaceFieldRead(f, clazz, readPrefix + name); 
213                  converter.replaceFieldWrite(f, clazz, writePrefix + name); 
214              } 
215          } 
216      } 
217   
218      private boolean modifyClassfile(CtClass clazz, CtClass metaobject, 
219                                      CtClass metaclass) 
220              throws CannotCompileException, NotFoundException { 
221          if (clazz.getAttribute("Reflective") != null) 
222              return false;       // this is already reflective. 
223          else 
224              clazz.setAttribute("Reflective", new byte[0]); 
225   
226          CtClass mlevel = classPool.get("javassist.reflect.Metalevel"); 
227          boolean addMeta = !clazz.subtypeOf(mlevel); 
228          if (addMeta) 
229              clazz.addInterface(mlevel); 
230   
231          processMethods(clazz, addMeta); 
232          processFields(clazz); 
233   
234          CtField f; 
235          if (addMeta) { 
236              f = new CtField(classPool.get("javassist.reflect.Metaobject"), 
237                      metaobjectField, clazz); 
238              f.setModifiers(Modifier.PROTECTED); 
239              clazz.addField(f, CtField.Initializer.byNewWithParams(metaobject)); 
240   
241              clazz.addMethod(CtNewMethod.getter(metaobjectGetter, f)); 
242              clazz.addMethod(CtNewMethod.setter(metaobjectSetter, f)); 
243          } 
244   
245          f = new CtField(classPool.get("javassist.reflect.ClassMetaobject"), 
246                  classobjectField, clazz); 
247          f.setModifiers(Modifier.PRIVATE | Modifier.STATIC); 
248          clazz.addField(f, CtField.Initializer.byNew(metaclass, 
249                  new String[]{clazz.getName()})); 
250   
251          clazz.addMethod(CtNewMethod.getter(classobjectAccessor, f)); 
252          return true; 
253      } 
254   
255      private void processMethods(CtClass clazz, boolean dontSearch) 
256              throws CannotCompileException, NotFoundException { 
257          CtMethod[] ms = clazz.getMethods(); 
258          int identifier = 0; 
259          for (int i = 0; i < ms.length; ++i) { 
260              CtMethod m = ms[i]; 
261              int mod = m.getModifiers(); 
262              if (Modifier.isPublic(mod) && !Modifier.isAbstract(mod)) 
263                  processMethods0(mod, clazz, m, i, dontSearch); 
264          } 
265      } 
266   
267      private void processMethods0(int mod, CtClass clazz, 
268                                   CtMethod m, int identifier, boolean dontSearch) 
269              throws CannotCompileException, NotFoundException { 
270          CtMethod body; 
271          String name = m.getName(); 
272   
273          if (isExcluded(name))   // internally-used method inherited 
274              return;             // from a reflective class. 
275   
276          CtMethod m2; 
277          if (m.getDeclaringClass() == clazz) { 
278              if (Modifier.isNative(mod)) 
279                  return; 
280   
281              m2 = m; 
282          } else { 
283              if (Modifier.isFinal(mod)) 
284                  return; 
285   
286              mod &= ~Modifier.NATIVE; 
287              m2 = CtNewMethod.delegator(findOriginal(m, dontSearch), clazz); 
288              m2.setModifiers(mod); 
289              clazz.addMethod(m2); 
290          } 
291   
292          m2.setName(ClassMetaobject.methodPrefix + identifier 
293                  + "_" + name); 
294   
295          if (Modifier.isStatic(mod)) 
296              body = trapStaticMethod; 
297          else 
298              body = trapMethod; 
299   
300          CtMethod wmethod 
301                  = CtNewMethod.wrapped(m.getReturnType(), name, 
302                          m.getParameterTypes(), m.getExceptionTypes(), 
303                          body, ConstParameter.integer(identifier), 
304                          clazz); 
305          wmethod.setModifiers(mod); 
306          clazz.addMethod(wmethod); 
307      } 
308   
309      private CtMethod findOriginal(CtMethod m, boolean dontSearch) 
310              throws NotFoundException { 
311          if (dontSearch) 
312              return m; 
313   
314          String name = m.getName(); 
315          CtMethod[] ms = m.getDeclaringClass().getDeclaredMethods(); 
316          for (int i = 0; i < ms.length; ++i) { 
317              String orgName = ms[i].getName(); 
318              if (orgName.endsWith(name) 
319                      && orgName.startsWith(ClassMetaobject.methodPrefix) 
320                      && ms[i].getSignature().equals(m.getSignature())) 
321                  return ms[i]; 
322          } 
323   
324          return m; 
325      } 
326   
327      private void processFields(CtClass clazz) 
328              throws CannotCompileException, NotFoundException { 
329          CtField[] fs = clazz.getDeclaredFields(); 
330          for (int i = 0; i < fs.length; ++i) { 
331              CtField f = fs[i]; 
332              int mod = f.getModifiers(); 
333              if ((mod & Modifier.PUBLIC) != 0 && (mod & Modifier.FINAL) == 0) { 
334                  mod |= Modifier.STATIC; 
335                  String name = f.getName(); 
336                  CtClass ftype = f.getType(); 
337                  CtMethod wmethod 
338                          = CtNewMethod.wrapped(ftype, readPrefix + name, 
339                                  readParam, null, trapRead, 
340                                  ConstParameter.string(name), 
341                                  clazz); 
342                  wmethod.setModifiers(mod); 
343                  clazz.addMethod(wmethod); 
344                  CtClass[] writeParam = new CtClass[2]; 
345                  writeParam[0] = classPool.get("java.lang.Object"); 
346                  writeParam[1] = ftype; 
347                  wmethod = CtNewMethod.wrapped(CtClass.voidType, 
348                          writePrefix + name, 
349                          writeParam, null, trapWrite, 
350                          ConstParameter.string(name), clazz); 
351                  wmethod.setModifiers(mod); 
352                  clazz.addMethod(wmethod); 
353              } 
354          } 
355      } 
356  } 
357