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