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