123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- /*
- * Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later,
- * or the Apache License Version 2.0.
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- */
-
- package javassist.tools.reflect;
-
- import java.util.Iterator;
- import javassist.*;
- import javassist.CtMethod.ConstParameter;
- import javassist.bytecode.ClassFile;
- import javassist.bytecode.BadBytecode;
- import javassist.bytecode.MethodInfo;
-
- /**
- * The class implementing the behavioral reflection mechanism.
- *
- * <p>If a class is reflective,
- * then all the method invocations on every
- * instance of that class are intercepted by the runtime
- * metaobject controlling that instance. The methods inherited from the
- * super classes are also intercepted except final methods. To intercept
- * a final method in a super class, that super class must be also reflective.
- *
- * <p>To do this, the original class file representing a reflective class:
- *
- * <pre>
- * class Person {
- * public int f(int i) { return i + 1; }
- * public int value;
- * }
- * </pre>
- *
- * <p>is modified so that it represents a class:
- *
- * <pre>
- * class Person implements Metalevel {
- * public int _original_f(int i) { return i + 1; }
- * public int f(int i) { <i>delegate to the metaobject</i> }
- *
- * public int value;
- * public int _r_value() { <i>read "value"</i> }
- * public void _w_value(int v) { <i>write "value"</i> }
- *
- * public ClassMetaobject _getClass() { <i>return a class metaobject</i> }
- * public Metaobject _getMetaobject() { <i>return a metaobject</i> }
- * public void _setMetaobject(Metaobject m) { <i>change a metaobject</i> }
- * }
- * </pre>
- *
- * @see javassist.tools.reflect.ClassMetaobject
- * @see javassist.tools.reflect.Metaobject
- * @see javassist.tools.reflect.Loader
- * @see javassist.tools.reflect.Compiler
- */
- public class Reflection implements Translator {
-
- static final String classobjectField = "_classobject";
- static final String classobjectAccessor = "_getClass";
- static final String metaobjectField = "_metaobject";
- static final String metaobjectGetter = "_getMetaobject";
- static final String metaobjectSetter = "_setMetaobject";
- static final String readPrefix = "_r_";
- static final String writePrefix = "_w_";
-
- static final String metaobjectClassName = "javassist.tools.reflect.Metaobject";
- static final String classMetaobjectClassName
- = "javassist.tools.reflect.ClassMetaobject";
-
- protected CtMethod trapMethod, trapStaticMethod;
- protected CtMethod trapRead, trapWrite;
- protected CtClass[] readParam;
-
- protected ClassPool classPool;
- protected CodeConverter converter;
-
- private boolean isExcluded(String name) {
- return name.startsWith(ClassMetaobject.methodPrefix)
- || name.equals(classobjectAccessor)
- || name.equals(metaobjectSetter)
- || name.equals(metaobjectGetter)
- || name.startsWith(readPrefix)
- || name.startsWith(writePrefix);
- }
-
- /**
- * Constructs a new <code>Reflection</code> object.
- */
- public Reflection() {
- classPool = null;
- converter = new CodeConverter();
- }
-
- /**
- * Initializes the object.
- */
- public void start(ClassPool pool) throws NotFoundException {
- classPool = pool;
- final String msg
- = "javassist.tools.reflect.Sample is not found or broken.";
- try {
- CtClass c = classPool.get("javassist.tools.reflect.Sample");
- rebuildClassFile(c.getClassFile());
- trapMethod = c.getDeclaredMethod("trap");
- trapStaticMethod = c.getDeclaredMethod("trapStatic");
- trapRead = c.getDeclaredMethod("trapRead");
- trapWrite = c.getDeclaredMethod("trapWrite");
- readParam
- = new CtClass[] { classPool.get("java.lang.Object") };
- }
- catch (NotFoundException e) {
- throw new RuntimeException(msg);
- } catch (BadBytecode e) {
- throw new RuntimeException(msg);
- }
- }
-
- /**
- * Inserts hooks for intercepting accesses to the fields declared
- * in reflective classes.
- */
- public void onLoad(ClassPool pool, String classname)
- throws CannotCompileException, NotFoundException
- {
- CtClass clazz = pool.get(classname);
- clazz.instrument(converter);
- }
-
- /**
- * Produces a reflective class.
- * If the super class is also made reflective, it must be done
- * before the sub class.
- *
- * @param classname the name of the reflective class
- * @param metaobject the class name of metaobjects.
- * @param metaclass the class name of the class metaobject.
- * @return <code>false</code> if the class is already reflective.
- *
- * @see javassist.tools.reflect.Metaobject
- * @see javassist.tools.reflect.ClassMetaobject
- */
- public boolean makeReflective(String classname,
- String metaobject, String metaclass)
- throws CannotCompileException, NotFoundException
- {
- return makeReflective(classPool.get(classname),
- classPool.get(metaobject),
- classPool.get(metaclass));
- }
-
- /**
- * Produces a reflective class.
- * If the super class is also made reflective, it must be done
- * before the sub class.
- *
- * @param clazz the reflective class.
- * @param metaobject the class of metaobjects.
- * It must be a subclass of
- * <code>Metaobject</code>.
- * @param metaclass the class of the class metaobject.
- * It must be a subclass of
- * <code>ClassMetaobject</code>.
- * @return <code>false</code> if the class is already reflective.
- *
- * @see javassist.tools.reflect.Metaobject
- * @see javassist.tools.reflect.ClassMetaobject
- */
- public boolean makeReflective(Class clazz,
- Class metaobject, Class metaclass)
- throws CannotCompileException, NotFoundException
- {
- return makeReflective(clazz.getName(), metaobject.getName(),
- metaclass.getName());
- }
-
- /**
- * Produces a reflective class. It modifies the given
- * <code>CtClass</code> object and makes it reflective.
- * If the super class is also made reflective, it must be done
- * before the sub class.
- *
- * @param clazz the reflective class.
- * @param metaobject the class of metaobjects.
- * It must be a subclass of
- * <code>Metaobject</code>.
- * @param metaclass the class of the class metaobject.
- * It must be a subclass of
- * <code>ClassMetaobject</code>.
- * @return <code>false</code> if the class is already reflective.
- *
- * @see javassist.tools.reflect.Metaobject
- * @see javassist.tools.reflect.ClassMetaobject
- */
- public boolean makeReflective(CtClass clazz,
- CtClass metaobject, CtClass metaclass)
- throws CannotCompileException, CannotReflectException,
- NotFoundException
- {
- if (clazz.isInterface())
- throw new CannotReflectException(
- "Cannot reflect an interface: " + clazz.getName());
-
- if (clazz.subclassOf(classPool.get(classMetaobjectClassName)))
- throw new CannotReflectException(
- "Cannot reflect a subclass of ClassMetaobject: "
- + clazz.getName());
-
- if (clazz.subclassOf(classPool.get(metaobjectClassName)))
- throw new CannotReflectException(
- "Cannot reflect a subclass of Metaobject: "
- + clazz.getName());
-
- registerReflectiveClass(clazz);
- return modifyClassfile(clazz, metaobject, metaclass);
- }
-
- /**
- * Registers a reflective class. The field accesses to the instances
- * of this class are instrumented.
- */
- private void registerReflectiveClass(CtClass clazz) {
- CtField[] fs = clazz.getDeclaredFields();
- for (int i = 0; i < fs.length; ++i) {
- CtField f = fs[i];
- int mod = f.getModifiers();
- if ((mod & Modifier.PUBLIC) != 0 && (mod & Modifier.FINAL) == 0) {
- String name = f.getName();
- converter.replaceFieldRead(f, clazz, readPrefix + name);
- converter.replaceFieldWrite(f, clazz, writePrefix + name);
- }
- }
- }
-
- private boolean modifyClassfile(CtClass clazz, CtClass metaobject,
- CtClass metaclass)
- throws CannotCompileException, NotFoundException
- {
- if (clazz.getAttribute("Reflective") != null)
- return false; // this is already reflective.
- else
- clazz.setAttribute("Reflective", new byte[0]);
-
- CtClass mlevel = classPool.get("javassist.tools.reflect.Metalevel");
- boolean addMeta = !clazz.subtypeOf(mlevel);
- if (addMeta)
- clazz.addInterface(mlevel);
-
- processMethods(clazz, addMeta);
- processFields(clazz);
-
- CtField f;
- if (addMeta) {
- f = new CtField(classPool.get("javassist.tools.reflect.Metaobject"),
- metaobjectField, clazz);
- f.setModifiers(Modifier.PROTECTED);
- clazz.addField(f, CtField.Initializer.byNewWithParams(metaobject));
-
- clazz.addMethod(CtNewMethod.getter(metaobjectGetter, f));
- clazz.addMethod(CtNewMethod.setter(metaobjectSetter, f));
- }
-
- f = new CtField(classPool.get("javassist.tools.reflect.ClassMetaobject"),
- classobjectField, clazz);
- f.setModifiers(Modifier.PRIVATE | Modifier.STATIC);
- clazz.addField(f, CtField.Initializer.byNew(metaclass,
- new String[] { clazz.getName() }));
-
- clazz.addMethod(CtNewMethod.getter(classobjectAccessor, f));
- return true;
- }
-
- private void processMethods(CtClass clazz, boolean dontSearch)
- throws CannotCompileException, NotFoundException
- {
- CtMethod[] ms = clazz.getMethods();
- for (int i = 0; i < ms.length; ++i) {
- CtMethod m = ms[i];
- int mod = m.getModifiers();
- if (Modifier.isPublic(mod) && !Modifier.isAbstract(mod))
- processMethods0(mod, clazz, m, i, dontSearch);
- }
- }
-
- private void processMethods0(int mod, CtClass clazz,
- CtMethod m, int identifier, boolean dontSearch)
- throws CannotCompileException, NotFoundException
- {
- CtMethod body;
- String name = m.getName();
-
- if (isExcluded(name)) // internally-used method inherited
- return; // from a reflective class.
-
- CtMethod m2;
- if (m.getDeclaringClass() == clazz) {
- if (Modifier.isNative(mod))
- return;
-
- m2 = m;
- if (Modifier.isFinal(mod)) {
- mod &= ~Modifier.FINAL;
- m2.setModifiers(mod);
- }
- }
- else {
- if (Modifier.isFinal(mod))
- return;
-
- mod &= ~Modifier.NATIVE;
- m2 = CtNewMethod.delegator(findOriginal(m, dontSearch), clazz);
- m2.setModifiers(mod);
- clazz.addMethod(m2);
- }
-
- m2.setName(ClassMetaobject.methodPrefix + identifier
- + "_" + name);
-
- if (Modifier.isStatic(mod))
- body = trapStaticMethod;
- else
- body = trapMethod;
-
- CtMethod wmethod
- = CtNewMethod.wrapped(m.getReturnType(), name,
- m.getParameterTypes(), m.getExceptionTypes(),
- body, ConstParameter.integer(identifier),
- clazz);
- wmethod.setModifiers(mod);
- clazz.addMethod(wmethod);
- }
-
- private CtMethod findOriginal(CtMethod m, boolean dontSearch)
- throws NotFoundException
- {
- if (dontSearch)
- return m;
-
- String name = m.getName();
- CtMethod[] ms = m.getDeclaringClass().getDeclaredMethods();
- for (int i = 0; i < ms.length; ++i) {
- String orgName = ms[i].getName();
- if (orgName.endsWith(name)
- && orgName.startsWith(ClassMetaobject.methodPrefix)
- && ms[i].getSignature().equals(m.getSignature()))
- return ms[i];
- }
-
- return m;
- }
-
- private void processFields(CtClass clazz)
- throws CannotCompileException, NotFoundException
- {
- CtField[] fs = clazz.getDeclaredFields();
- for (int i = 0; i < fs.length; ++i) {
- CtField f = fs[i];
- int mod = f.getModifiers();
- if ((mod & Modifier.PUBLIC) != 0 && (mod & Modifier.FINAL) == 0) {
- mod |= Modifier.STATIC;
- String name = f.getName();
- CtClass ftype = f.getType();
- CtMethod wmethod
- = CtNewMethod.wrapped(ftype, readPrefix + name,
- readParam, null, trapRead,
- ConstParameter.string(name),
- clazz);
- wmethod.setModifiers(mod);
- clazz.addMethod(wmethod);
- CtClass[] writeParam = new CtClass[2];
- writeParam[0] = classPool.get("java.lang.Object");
- writeParam[1] = ftype;
- wmethod = CtNewMethod.wrapped(CtClass.voidType,
- writePrefix + name,
- writeParam, null, trapWrite,
- ConstParameter.string(name), clazz);
- wmethod.setModifiers(mod);
- clazz.addMethod(wmethod);
- }
- }
- }
-
- public void rebuildClassFile(ClassFile cf) throws BadBytecode {
- if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_6)
- return;
-
- Iterator methods = cf.getMethods().iterator();
- while (methods.hasNext()) {
- MethodInfo mi = (MethodInfo)methods.next();
- mi.rebuildStackMap(classPool);
- }
- }
- }
|