/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