diff options
Diffstat (limited to 'src/main/javassist/reflect/Reflection.java')
-rw-r--r-- | src/main/javassist/reflect/Reflection.java | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/src/main/javassist/reflect/Reflection.java b/src/main/javassist/reflect/Reflection.java new file mode 100644 index 00000000..f76b396e --- /dev/null +++ b/src/main/javassist/reflect/Reflection.java @@ -0,0 +1,375 @@ +/* + * This file is part of the Javassist toolkit. + * + * 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. You may obtain a copy of the License at + * either http://www.mozilla.org/MPL/. + * + * 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. + * + * The Original Code is Javassist. + * + * The Initial Developer of the Original Code is Shigeru Chiba. Portions + * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba. + * All Rights Reserved. + * + * Contributor(s): + * + * The development of this software is supported in part by the PRESTO + * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation. + */ + +package javassist.reflect; + +import javassist.*; +import java.io.IOException; +import javassist.CtMethod.ConstParameter; + +/** + * The class implementing the reflection mechanism. + * + * <p>This class is used with <code>ClassPool</code>. + * Note that it implements an interface <code>javassist.Translator</code>. + * + * <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. + * To do this, the original class file representing a reflective class: + * + * <ul><pre> + * class Person { + * public int f(int i) { return i + 1; } + * public int value; + * } + * </pre></ul> + * + * <p>is modified so that it represents a class: + * + * <ul><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></ul> + * + * @see javassist.reflect.ClassMetaobject + * @see javassist.reflect.Metaobject + * @see javassist.reflect.Loader + * @see javassist.reflect.Compiler + * @see javassist.ClassPool + * @see javassist.Translator + */ +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_"; + + 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. + */ + public void start(ClassPool pool) throws NotFoundException { + classPool = pool; + final String msg + = "javassist.reflect.Sample is not found or broken."; + try { + CtClass c = classPool.get("javassist.reflect.Sample"); + 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); + } + } + + /** + * Inserts hooks for intercepting accesses to the fields declared + * in reflective classes. + */ + public void onWrite(ClassPool pool, String classname) + throws CannotCompileException, NotFoundException + { + CtClass c = pool.get(classname); + c.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.reflect.Metaobject + * @see javassist.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.reflect.Metaobject + * @see javassist.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.reflect.Metaobject + * @see javassist.reflect.ClassMetaobject + */ + public boolean makeReflective(CtClass clazz, + CtClass metaobject, CtClass metaclass) + throws CannotCompileException, NotFoundException + { + 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.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.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.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(); + int identifier = 0; + 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; + } + 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); + } + } + } +} |