Home | History | Annotate | Download | only in reflect
      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 
     16 package javassist.tools.reflect;
     17 
     18 import javassist.*;
     19 import 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  */
     62 public 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