123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699 |
- /*
- * Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2005 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.
- *
- * 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.util.proxy;
-
- import java.lang.reflect.Field;
- import java.lang.reflect.Method;
- import java.lang.reflect.Constructor;
- import java.lang.reflect.Member;
- import java.lang.reflect.Modifier;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.Map;
- import java.util.Set;
-
- import javassist.CannotCompileException;
- import javassist.bytecode.*;
-
- /**
- * Factory of dynamic proxy classes.
- *
- * <p>This factory generates a class that extends the given super class and implements
- * the given interfaces. The calls of the methods inherited from the super class are
- * forwarded and then <code>invoke()</code> is called on the method handler
- * associated with the generated class. The calls of the methods from the interfaces
- * are also forwarded to the method handler.
- *
- * <p>For example, if the following code is executed,
- *
- * <ul><pre>
- * ProxyFactory f = new ProxyFactory();
- * f.setSuperclass(Foo.class);
- * MethodHandler mi = new MethodHandler() {
- * public Object invoke(Object self, Method m, Method proceed,
- * Object[] args) throws Exception {
- * System.out.println("Name: " + m.getName());
- * proceed.invoke(self, args); // execute the original method.
- * }
- * };
- * f.setHandler(mi);
- * Class c = f.createClass();
- * Foo foo = (Foo)c.newInstance();
- * </pre></ul>
- *
- * <p>Then, the following method call will be forwarded to MethodHandler
- * <code>mi</code> and prints a message before executing the originally called method
- * <code>bar()</code> in <code>Foo</code>.
- *
- * <ul><pre>
- * foo.bar();
- * </pre></ul>
- *
- * <p>To change the method handler during runtime,
- * execute the following code:
- *
- * <ul><pre>
- * MethodHandler mi2 = ... ; // another handler
- * ((ProxyObject)foo).setHandler(mi2);
- * </pre></ul>
- *
- * <p>Here is an example of method handler. It does not execute
- * anything except invoking the original method:
- *
- * <ul><pre>
- * class SimpleHandler implements MethodHandler {
- * public Object invoke(Object self, Method m,
- * Method proceed, Object[] args) throws Exception {
- * return proceed.invoke(self, args);
- * }
- * }
- * </pre></ul>
- *
- * @see MethodHandler
- * @since 3.1
- */
- public class ProxyFactory {
- private Class superClass;
- private Class[] interfaces;
- private MethodFilter methodFilter;
- private MethodHandler handler;
- private Class thisClass;
-
- /**
- * If the value of this variable is not null, the class file of
- * the generated proxy class is written under the directory specified
- * by this variable. For example, if the value is
- * <code>"."</code>, then the class file is written under the current
- * directory. This method is for debugging.
- *
- * <p>The default value is null.
- */
- public String writeDirectory;
-
- private static final Class OBJECT_TYPE = Object.class;
-
- private static final String HOLDER = "_methods_";
- private static final String HOLDER_TYPE = "[Ljava/lang/reflect/Method;";
- private static final String HANDLER = "handler";
- private static final String DEFAULT_INTERCEPTOR = "default_interceptor";
- private static final String HANDLER_TYPE
- = 'L' + MethodHandler.class.getName().replace('.', '/') + ';';
- private static final String HANDLER_SETTER = "setHandler";
- private static final String HANDLER_SETTER_TYPE = "(" + HANDLER_TYPE + ")V";
-
- /**
- * Constructs a factory of proxy class.
- */
- public ProxyFactory() {
- superClass = null;
- interfaces = null;
- methodFilter = null;
- handler = new MethodHandler() {
- public Object invoke(Object self, Method m,
- Method proceed, Object[] args)
- throws Exception
- {
- return proceed.invoke(self, args);
- }
- };
- thisClass = null;
- writeDirectory = null;
- }
-
- /**
- * Sets the super class of a proxy class.
- */
- public void setSuperclass(Class clazz) {
- superClass = clazz;
- }
-
- /**
- * Sets the interfaces of a proxy class.
- */
- public void setInterfaces(Class[] ifs) {
- interfaces = ifs;
- }
-
- /**
- * Sets a filter that selects the methods that will be controlled by a handler.
- */
- public void setFilter(MethodFilter mf) {
- methodFilter = mf;
- }
-
- /**
- * Generates a proxy class.
- */
- public Class createClass() {
- if (thisClass == null)
- try {
- ClassFile cf = make();
- ClassLoader cl = getClassLoader();
- if (writeDirectory != null)
- FactoryHelper.writeFile(cf, writeDirectory);
-
- thisClass = FactoryHelper.toClass(cf, cl);
- setHandler();
- }
- catch (CannotCompileException e) {
- throw new RuntimeException(e.getMessage(), e);
- }
-
- return thisClass;
- }
-
- protected ClassLoader getClassLoader() {
- if (superClass != null && !superClass.getName().equals("java.lang.Object"))
- return superClass.getClassLoader();
- else if (interfaces != null && interfaces.length > 0)
- return interfaces[0].getClassLoader();
- else
- return this.getClass().getClassLoader();
- // return Thread.currentThread().getContextClassLoader();
- }
-
- /**
- * Sets the default invocation handler. This invocation handler is shared
- * among all the instances of a proxy class unless another is explicitly
- * specified.
- */
- public void setHandler(MethodHandler mi) {
- handler = mi;
- setHandler();
- }
-
- private void setHandler() {
- if (thisClass != null && handler != null)
- try {
- Field f = thisClass.getField(DEFAULT_INTERCEPTOR);
- f.setAccessible(true);
- f.set(null, handler);
- f.setAccessible(false);
- }
- catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- private static int counter = 0;
-
- private ClassFile make() throws CannotCompileException {
- String superName, classname;
- if (interfaces == null)
- interfaces = new Class[0];
-
- if (superClass == null) {
- superClass = OBJECT_TYPE;
- superName = superClass.getName();
- classname = interfaces.length == 0 ? superName
- : interfaces[0].getName();
- }
- else {
- superName = superClass.getName();
- classname = superName;
- }
-
- if (Modifier.isFinal(superClass.getModifiers()))
- throw new CannotCompileException(superName + " is final");
-
- // generate a proxy name.
- classname = classname + "_$$_javassist_" + counter++;
- if (classname.startsWith("java."))
- classname = "org.javassist.tmp." + classname;
-
- ClassFile cf = new ClassFile(false, classname, superName);
- cf.setAccessFlags(AccessFlag.PUBLIC);
- setInterfaces(cf, interfaces);
- ConstPool pool = cf.getConstPool();
- FieldInfo finfo = new FieldInfo(pool, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
- finfo.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
- cf.addField(finfo);
-
- FieldInfo finfo2 = new FieldInfo(pool, HANDLER, HANDLER_TYPE);
- finfo2.setAccessFlags(AccessFlag.PRIVATE);
- cf.addField(finfo2);
-
- HashMap allMethods = getMethods(superClass, interfaces);
- int size = allMethods.size();
- makeConstructors(classname, cf, pool, classname);
- int s = overrideMethods(cf, pool, classname, allMethods);
- addMethodsHolder(cf, pool, classname, s);
- addSetter(classname, cf, pool);
-
- thisClass = null;
- return cf;
- }
-
- private static void setInterfaces(ClassFile cf, Class[] interfaces) {
- String setterIntf = ProxyObject.class.getName();
- String[] list;
- if (interfaces == null || interfaces.length == 0)
- list = new String[] { setterIntf };
- else {
- list = new String[interfaces.length + 1];
- for (int i = 0; i < interfaces.length; i++)
- list[i] = interfaces[i].getName();
-
- list[interfaces.length] = setterIntf;
- }
-
- cf.setInterfaces(list);
- }
-
- private static void addMethodsHolder(ClassFile cf, ConstPool cp,
- String classname, int size)
- throws CannotCompileException
- {
- FieldInfo finfo = new FieldInfo(cp, HOLDER, HOLDER_TYPE);
- finfo.setAccessFlags(AccessFlag.PRIVATE | AccessFlag.STATIC);
- cf.addField(finfo);
- MethodInfo minfo = new MethodInfo(cp, "<clinit>", "()V");
- Bytecode code = new Bytecode(cp, 0, 0);
- code.addIconst(size * 2);
- code.addAnewarray("java.lang.reflect.Method");
- code.addPutstatic(classname, HOLDER, HOLDER_TYPE);
- code.addOpcode(Bytecode.RETURN);
- minfo.setCodeAttribute(code.toCodeAttribute());
- cf.addMethod(minfo);
- }
-
- private static void addSetter(String classname, ClassFile cf, ConstPool cp)
- throws CannotCompileException
- {
- MethodInfo minfo = new MethodInfo(cp, HANDLER_SETTER,
- HANDLER_SETTER_TYPE);
- minfo.setAccessFlags(AccessFlag.PUBLIC);
- Bytecode code = new Bytecode(cp, 2, 2);
- code.addAload(0);
- code.addAload(1);
- code.addPutfield(classname, HANDLER, HANDLER_TYPE);
- code.addOpcode(Bytecode.RETURN);
- minfo.setCodeAttribute(code.toCodeAttribute());
- cf.addMethod(minfo);
- }
-
- private int overrideMethods(ClassFile cf, ConstPool cp, String className,
- HashMap allMethods)
- throws CannotCompileException
- {
- String prefix = makeUniqueName("_d", allMethods);
- Set entries = allMethods.entrySet();
- Iterator it = entries.iterator();
- int index = 0;
- while (it.hasNext()) {
- Map.Entry e = (Map.Entry)it.next();
- String key = (String)e.getKey();
- Method meth = (Method)e.getValue();
- int mod = meth.getModifiers();
- if (!Modifier.isFinal(mod) && !Modifier.isStatic(mod)
- && isVisible(mod, className, meth))
- if (methodFilter == null || methodFilter.isHandled(meth))
- override(className, meth, prefix, index++,
- keyToDesc(key), cf, cp);
- }
-
- return index;
- }
-
- private void override(String thisClassname, Method meth, String prefix,
- int index, String desc, ClassFile cf, ConstPool cp)
- throws CannotCompileException
- {
- Class declClass = meth.getDeclaringClass();
- String delegatorName = prefix + index + meth.getName();
- if (Modifier.isAbstract(meth.getModifiers()))
- delegatorName = null;
- else {
- MethodInfo delegator
- = makeDelegator(meth, desc, cp, declClass, delegatorName);
- cf.addMethod(delegator);
- }
-
- MethodInfo forwarder
- = makeForwarder(thisClassname, meth, desc, cp, declClass,
- delegatorName, index);
- cf.addMethod(forwarder);
- }
-
- private void makeConstructors(String thisClassName, ClassFile cf,
- ConstPool cp, String classname) throws CannotCompileException
- {
- Constructor[] cons = superClass.getDeclaredConstructors();
- for (int i = 0; i < cons.length; i++) {
- Constructor c = cons[i];
- int mod = c.getModifiers();
- if (!Modifier.isFinal(mod) && !Modifier.isPrivate(mod)
- && isVisible(mod, classname, c)) {
- MethodInfo m = makeConstructor(thisClassName, c, cp, superClass);
- cf.addMethod(m);
- }
- }
- }
-
- private static String makeUniqueName(String name, HashMap hash) {
- Set keys = hash.keySet();
- if (makeUniqueName0(name, keys.iterator()))
- return name;
-
- for (int i = 100; i < 999; i++) {
- String s = name + i;
- if (makeUniqueName0(s, keys.iterator()))
- return s;
- }
-
- throw new RuntimeException("cannot make a unique method name");
- }
-
- private static boolean makeUniqueName0(String name, Iterator it) {
- while (it.hasNext()) {
- String key = (String)it.next();
- if (key.startsWith(name))
- return false;
- }
-
- return true;
- }
-
- /**
- * Returns true if the method is visible from the class.
- *
- * @param mod the modifiers of the method.
- */
- private static boolean isVisible(int mod, String from, Member meth) {
- if ((mod & Modifier.PRIVATE) != 0)
- return false;
- else if ((mod & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0)
- return true;
- else {
- String p = getPackageName(from);
- String q = getPackageName(meth.getDeclaringClass().getName());
- if (p == null)
- return q == null;
- else
- return p.equals(q);
- }
- }
-
- private static String getPackageName(String name) {
- int i = name.lastIndexOf('.');
- if (i < 0)
- return null;
- else
- return name.substring(0, i);
- }
-
- private static HashMap getMethods(Class superClass, Class[] interfaceTypes) {
- HashMap hash = new HashMap();
- for (int i = 0; i < interfaceTypes.length; i++)
- getMethods(hash, interfaceTypes[i]);
-
- getMethods(hash, superClass);
- return hash;
- }
-
- private static void getMethods(HashMap hash, Class clazz) {
- Class[] ifs = clazz.getInterfaces();
- for (int i = 0; i < ifs.length; i++)
- getMethods(hash, ifs[i]);
-
- Class parent = clazz.getSuperclass();
- if (parent != null)
- getMethods(hash, parent);
-
- Method[] methods = clazz.getDeclaredMethods();
- for (int i = 0; i < methods.length; i++)
- if (!Modifier.isPrivate(methods[i].getModifiers())) {
- Method m = methods[i];
- String key = m.getName() + ':' + RuntimeSupport.makeDescriptor(m);
- hash.put(key, methods[i]);
- }
- }
-
- private static String keyToDesc(String key) {
- return key.substring(key.indexOf(':') + 1);
- }
-
- private static MethodInfo makeConstructor(String thisClassName, Constructor cons,
- ConstPool cp, Class superClass) {
- String desc = RuntimeSupport.makeDescriptor(cons.getParameterTypes(),
- Void.TYPE);
- MethodInfo minfo = new MethodInfo(cp, "<init>", desc);
- minfo.setAccessFlags(Modifier.PUBLIC); // cons.getModifiers() & ~Modifier.NATIVE
- setThrows(minfo, cp, cons.getExceptionTypes());
- Bytecode code = new Bytecode(cp, 0, 0);
- code.addAload(0);
- code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
- code.addPutfield(thisClassName, HANDLER, HANDLER_TYPE);
- code.addAload(0);
- int s = addLoadParameters(code, cons.getParameterTypes(), 1);
- code.addInvokespecial(superClass.getName(), "<init>", desc);
- code.addOpcode(Opcode.RETURN);
- code.setMaxLocals(++s);
- minfo.setCodeAttribute(code.toCodeAttribute());
- return minfo;
- }
-
- private static MethodInfo makeDelegator(Method meth, String desc,
- ConstPool cp, Class declClass, String delegatorName) {
- MethodInfo delegator = new MethodInfo(cp, delegatorName, desc);
- delegator.setAccessFlags(Modifier.FINAL | Modifier.PUBLIC
- | (meth.getModifiers() & ~(Modifier.PRIVATE
- | Modifier.PROTECTED
- | Modifier.ABSTRACT
- | Modifier.NATIVE
- | Modifier.SYNCHRONIZED)));
- setThrows(delegator, cp, meth);
- Bytecode code = new Bytecode(cp, 0, 0);
- code.addAload(0);
- int s = addLoadParameters(code, meth.getParameterTypes(), 1);
- code.addInvokespecial(declClass.getName(), meth.getName(), desc);
- addReturn(code, meth.getReturnType());
- code.setMaxLocals(++s);
- delegator.setCodeAttribute(code.toCodeAttribute());
- return delegator;
- }
-
- /**
- * @param delegatorName null if the original method is abstract.
- */
- private static MethodInfo makeForwarder(String thisClassName,
- Method meth, String desc, ConstPool cp,
- Class declClass, String delegatorName, int index) {
- MethodInfo forwarder = new MethodInfo(cp, meth.getName(), desc);
- forwarder.setAccessFlags(Modifier.FINAL
- | (meth.getModifiers() & ~(Modifier.ABSTRACT
- | Modifier.NATIVE
- | Modifier.SYNCHRONIZED)));
- setThrows(forwarder, cp, meth);
- int args = Descriptor.paramSize(desc);
- Bytecode code = new Bytecode(cp, 0, args + 2);
- /*
- * if (methods[index * 2] == null) {
- * methods[index * 2]
- * = RuntimeSupport.findMethod(this, <overridden name>, <desc>);
- * methods[index * 2 + 1]
- * = RuntimeSupport.findMethod(this, <delegator name>, <desc>);
- * or = null // the original method is abstract.
- * }
- * return ($r)handler.invoke(this, methods[index * 2],
- * methods[index * 2 + 1], $args);
- */
- int origIndex = index * 2;
- int delIndex = index * 2 + 1;
- int arrayVar = args + 1;
- code.addGetstatic(thisClassName, HOLDER, HOLDER_TYPE);
- code.addAstore(arrayVar);
- code.addAload(arrayVar);
- code.addIconst(origIndex);
- code.addOpcode(Opcode.AALOAD);
- code.addOpcode(Opcode.IFNONNULL);
- int pc = code.currentPc();
- code.addIndex(0);
-
- callFindMethod(code, "findSuperMethod", arrayVar, origIndex, meth.getName(), desc);
- callFindMethod(code, "findMethod", arrayVar, delIndex, delegatorName, desc);
-
- code.write16bit(pc, code.currentPc() - pc + 1);
- code.addAload(0);
- code.addGetfield(thisClassName, HANDLER, HANDLER_TYPE);
- code.addAload(0);
-
- code.addAload(arrayVar);
- code.addIconst(origIndex);
- code.addOpcode(Opcode.AALOAD);
-
- code.addAload(arrayVar);
- code.addIconst(delIndex);
- code.addOpcode(Opcode.AALOAD);
-
- makeParameterList(code, meth.getParameterTypes());
- code.addInvokeinterface(MethodHandler.class.getName(), "invoke",
- "(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;",
- 5);
- Class retType = meth.getReturnType();
- addUnwrapper(code, retType);
- addReturn(code, retType);
-
- forwarder.setCodeAttribute(code.toCodeAttribute());
- return forwarder;
- }
-
- private static void setThrows(MethodInfo minfo, ConstPool cp, Method orig) {
- Class[] exceptions = orig.getExceptionTypes();
- setThrows(minfo, cp, exceptions);
- }
-
- private static void setThrows(MethodInfo minfo, ConstPool cp,
- Class[] exceptions) {
- if (exceptions.length == 0)
- return;
-
- String[] list = new String[exceptions.length];
- for (int i = 0; i < exceptions.length; i++)
- list[i] = exceptions[i].getName();
-
- ExceptionsAttribute ea = new ExceptionsAttribute(cp);
- ea.setExceptions(list);
- minfo.setExceptionsAttribute(ea);
- }
-
- private static int addLoadParameters(Bytecode code, Class[] params,
- int offset) {
- int stacksize = 0;
- int n = params.length;
- for (int i = 0; i < n; ++i)
- stacksize += addLoad(code, stacksize + offset, params[i]);
-
- return stacksize;
- }
-
- private static int addLoad(Bytecode code, int n, Class type) {
- if (type.isPrimitive()) {
- if (type == Long.TYPE) {
- code.addLload(n);
- return 2;
- }
- else if (type == Float.TYPE)
- code.addFload(n);
- else if (type == Double.TYPE) {
- code.addDload(n);
- return 2;
- }
- else
- code.addIload(n);
- }
- else
- code.addAload(n);
-
- return 1;
- }
-
- private static int addReturn(Bytecode code, Class type) {
- if (type.isPrimitive()) {
- if (type == Long.TYPE) {
- code.addOpcode(Opcode.LRETURN);
- return 2;
- }
- else if (type == Float.TYPE)
- code.addOpcode(Opcode.FRETURN);
- else if (type == Double.TYPE) {
- code.addOpcode(Opcode.DRETURN);
- return 2;
- }
- else if (type == Void.TYPE) {
- code.addOpcode(Opcode.RETURN);
- return 0;
- }
- else
- code.addOpcode(Opcode.IRETURN);
- }
- else
- code.addOpcode(Opcode.ARETURN);
-
- return 1;
- }
-
- private static void makeParameterList(Bytecode code, Class[] params) {
- int regno = 1;
- int n = params.length;
- code.addIconst(n);
- code.addAnewarray("java/lang/Object");
- for (int i = 0; i < n; i++) {
- code.addOpcode(Opcode.DUP);
- code.addIconst(i);
- Class type = params[i];
- if (type.isPrimitive())
- regno = makeWrapper(code, type, regno);
- else {
- code.addAload(regno);
- regno++;
- }
-
- code.addOpcode(Opcode.AASTORE);
- }
- }
-
- private static int makeWrapper(Bytecode code, Class type, int regno) {
- int index = FactoryHelper.typeIndex(type);
- String wrapper = FactoryHelper.wrapperTypes[index];
- code.addNew(wrapper);
- code.addOpcode(Opcode.DUP);
- addLoad(code, regno, type);
- code.addInvokespecial(wrapper, "<init>",
- FactoryHelper.wrapperDesc[index]);
- return regno + FactoryHelper.dataSize[index];
- }
-
- /**
- * @param methodName might be null.
- */
- private static void callFindMethod(Bytecode code, String findMethod,
- int arrayVar, int index, String methodName, String desc) {
- String findClass = RuntimeSupport.class.getName();
- String findDesc
- = "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/reflect/Method;";
-
- code.addAload(arrayVar);
- code.addIconst(index);
- if (methodName == null)
- code.addOpcode(Opcode.ACONST_NULL);
- else {
- code.addAload(0);
- code.addLdc(methodName);
- code.addLdc(desc);
- code.addInvokestatic(findClass, findMethod, findDesc);
- }
-
- code.addOpcode(Opcode.AASTORE);
- }
-
- private static void addUnwrapper(Bytecode code, Class type) {
- if (type.isPrimitive()) {
- if (type == Void.TYPE)
- code.addOpcode(Opcode.POP);
- else {
- int index = FactoryHelper.typeIndex(type);
- String wrapper = FactoryHelper.wrapperTypes[index];
- code.addCheckcast(wrapper);
- code.addInvokevirtual(wrapper,
- FactoryHelper.unwarpMethods[index],
- FactoryHelper.unwrapDesc[index]);
- }
- }
- else
- code.addCheckcast(type.getName());
- }
- }
|