0001: /*
0002: * Javassist, a Java-bytecode translator toolkit.
0003: * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
0004: *
0005: * The contents of this file are subject to the Mozilla Public License Version
0006: * 1.1 (the "License"); you may not use this file except in compliance with
0007: * the License. Alternatively, the contents of this file may be used under
0008: * the terms of the GNU Lesser General Public License Version 2.1 or later.
0009: *
0010: * Software distributed under the License is distributed on an "AS IS" basis,
0011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0012: * for the specific language governing rights and limitations under the
0013: * License.
0014: */
0015:
0016: package javassist.util.proxy;
0017:
0018: import java.lang.reflect.Field;
0019: import java.lang.reflect.InvocationTargetException;
0020: import java.lang.reflect.Method;
0021: import java.lang.reflect.Constructor;
0022: import java.lang.reflect.Member;
0023: import java.lang.reflect.Modifier;
0024: import java.security.ProtectionDomain;
0025: import java.util.HashMap;
0026: import java.util.WeakHashMap;
0027: import java.util.Iterator;
0028: import java.util.Map;
0029: import java.util.Set;
0030: import java.lang.ref.WeakReference;
0031:
0032: import javassist.CannotCompileException;
0033: import javassist.bytecode.*;
0034:
0035: /*
0036: * This class is implemented only with the lower-level API of Javassist.
0037: * This design decision is for maximizing performance.
0038: */
0039:
0040: /**
0041: * Factory of dynamic proxy classes.
0042: *
0043: * <p>This factory generates a class that extends the given super class and implements
0044: * the given interfaces. The calls of the methods inherited from the super class are
0045: * forwarded and then <code>invoke()</code> is called on the method handler
0046: * associated with the generated class. The calls of the methods from the interfaces
0047: * are also forwarded to the method handler.
0048: *
0049: * <p>For example, if the following code is executed,
0050: *
0051: * <ul><pre>
0052: * ProxyFactory f = new ProxyFactory();
0053: * f.setSuperclass(Foo.class);
0054: * MethodHandler mi = new MethodHandler() {
0055: * public Object invoke(Object self, Method m, Method proceed,
0056: * Object[] args) throws Throwable {
0057: * System.out.println("Name: " + m.getName());
0058: * return proceed.invoke(self, args); // execute the original method.
0059: * }
0060: * };
0061: * f.setFilter(new MethodFilter() {
0062: * public boolean isHandled(Method m) {
0063: * // ignore finalize()
0064: * return !m.getName().equals("finalize");
0065: * }
0066: * });
0067: * Class c = f.createClass();
0068: * Foo foo = (Foo)c.newInstance();
0069: * ((ProxyObject)foo).setHandler(mi);
0070: * </pre></ul>
0071: *
0072: * <p>Then, the following method call will be forwarded to MethodHandler
0073: * <code>mi</code> and prints a message before executing the originally called method
0074: * <code>bar()</code> in <code>Foo</code>.
0075: *
0076: * <ul><pre>
0077: * foo.bar();
0078: * </pre></ul>
0079: *
0080: * <p>The last three lines of the code shown above can be replaced with a call to
0081: * the helper method <code>create</code>, which generates a proxy class, instantiates
0082: * it, and sets the method handler of the instance:
0083: *
0084: * <ul><pre>
0085: * :
0086: * Foo foo = (Foo)f.create(new Class[0], new Object[0], mi);
0087: * </pre></ul>
0088: *
0089: * <p>To change the method handler during runtime,
0090: * execute the following code:
0091: *
0092: * <ul><pre>
0093: * MethodHandler mi2 = ... ; // another handler
0094: * ((ProxyObject)foo).setHandler(mi2);
0095: * </pre></ul>
0096: *
0097: * <p>You can also specify the default method handler:
0098: *
0099: * <ul><pre>
0100: * ProxyFactory f2 = new ProxyFactory();
0101: * f2.setSuperclass(Foo.class);
0102: * f2.setHandler(mi); // set the default handler
0103: * Class c2 = f2.createClass();
0104: * </pre></ul>
0105: *
0106: * <p>The default handler is implicitly attached to an instance of the generated class
0107: * <code>c2</code>. Calling <code>setHandler</code> on the instance is not necessary
0108: * unless another method handler must be attached to the instance.
0109: *
0110: * <p>The following code is an example of method handler. It does not execute
0111: * anything except invoking the original method:
0112: *
0113: * <ul><pre>
0114: * class SimpleHandler implements MethodHandler {
0115: * public Object invoke(Object self, Method m,
0116: * Method proceed, Object[] args) throws Exception {
0117: * return proceed.invoke(self, args);
0118: * }
0119: * }
0120: * </pre></ul>
0121: *
0122: * <p>A proxy object generated by <code>ProxyFactory</code> is serializable
0123: * if its super class or interfaces implement a <code>java.io.Serializable</code>.
0124: * However, a serialized proxy object will not be compatible with future releases.
0125: * The serialization support should be used for short-term storage or RMI.
0126: *
0127: * @see MethodHandler
0128: * @since 3.1
0129: */
0130: public class ProxyFactory {
0131: private Class super Class;
0132: private Class[] interfaces;
0133: private MethodFilter methodFilter;
0134: private MethodHandler handler;
0135: private Class this Class;
0136:
0137: /**
0138: * If the value of this variable is not null, the class file of
0139: * the generated proxy class is written under the directory specified
0140: * by this variable. For example, if the value is
0141: * <code>"."</code>, then the class file is written under the current
0142: * directory. This method is for debugging.
0143: *
0144: * <p>The default value is null.
0145: */
0146: public String writeDirectory;
0147:
0148: private static final Class OBJECT_TYPE = Object.class;
0149:
0150: private static final String HOLDER = "_methods_";
0151: private static final String HOLDER_TYPE = "[Ljava/lang/reflect/Method;";
0152: private static final String METHOD_FILTER_FIELD = "_method_filter";
0153: private static final String HANDLER = "handler";
0154: private static final String NULL_INTERCEPTOR_HOLDER = "javassist.util.proxy.RuntimeSupport";
0155: private static final String DEFAULT_INTERCEPTOR = "default_interceptor";
0156: private static final String HANDLER_TYPE = 'L' + MethodHandler.class
0157: .getName().replace('.', '/') + ';';
0158: private static final String HANDLER_SETTER = "setHandler";
0159: private static final String HANDLER_SETTER_TYPE = "("
0160: + HANDLER_TYPE + ")V";
0161:
0162: /**
0163: * If true, a generated proxy class is cached and it will be reused
0164: * when generating the proxy class with the same properties is requested.
0165: * The default value is true.
0166: *
0167: * @since 3.4
0168: */
0169: public static boolean useCache = true;
0170:
0171: private static WeakHashMap proxyCache = new WeakHashMap();
0172:
0173: static class CacheKey {
0174: String classes;
0175: MethodFilter filter;
0176: private int hash;
0177: WeakReference proxyClass;
0178: MethodHandler handler;
0179:
0180: public CacheKey(Class super Class, Class[] interfaces,
0181: MethodFilter f, MethodHandler h) {
0182: classes = getKey(super Class, interfaces);
0183: hash = classes.hashCode();
0184: filter = f;
0185: handler = h;
0186: proxyClass = null;
0187: }
0188:
0189: public int hashCode() {
0190: return hash;
0191: }
0192:
0193: public boolean equals(Object obj) {
0194: if (obj instanceof CacheKey) {
0195: CacheKey target = (CacheKey) obj;
0196: return target.filter == filter
0197: && target.handler == handler
0198: && target.classes.equals(classes);
0199: } else
0200: return false;
0201: }
0202:
0203: static String getKey(Class super Class, Class[] interfaces) {
0204: StringBuffer sbuf = new StringBuffer();
0205: if (super Class != null)
0206: sbuf.append(super Class.getName());
0207: sbuf.append(':');
0208: if (interfaces != null) {
0209: int len = interfaces.length;
0210: for (int i = 0; i < len; i++)
0211: sbuf.append(interfaces[i].getName()).append(',');
0212: }
0213:
0214: return sbuf.toString();
0215: }
0216: }
0217:
0218: /**
0219: * Constructs a factory of proxy class.
0220: */
0221: public ProxyFactory() {
0222: super Class = null;
0223: interfaces = null;
0224: methodFilter = null;
0225: handler = null;
0226: this Class = null;
0227: writeDirectory = null;
0228: }
0229:
0230: /**
0231: * Sets the super class of a proxy class.
0232: */
0233: public void setSuperclass(Class clazz) {
0234: super Class = clazz;
0235: }
0236:
0237: /**
0238: * Obtains the super class set by <code>setSuperclass()</code>.
0239: *
0240: * @since 3.4
0241: */
0242: public Class getSuperclass() {
0243: return super Class;
0244: }
0245:
0246: /**
0247: * Sets the interfaces of a proxy class.
0248: */
0249: public void setInterfaces(Class[] ifs) {
0250: interfaces = ifs;
0251: }
0252:
0253: /**
0254: * Obtains the interfaces set by <code>setInterfaces</code>.
0255: *
0256: * @since 3.4
0257: */
0258: public Class[] getInterfaces() {
0259: return interfaces;
0260: }
0261:
0262: /**
0263: * Sets a filter that selects the methods that will be controlled by a handler.
0264: */
0265: public void setFilter(MethodFilter mf) {
0266: methodFilter = mf;
0267: }
0268:
0269: /**
0270: * Generates a proxy class.
0271: */
0272: public Class createClass() {
0273: if (this Class == null) {
0274: ClassLoader cl = getClassLoader();
0275: synchronized (proxyCache) {
0276: if (useCache)
0277: createClass2(cl);
0278: else
0279: createClass3(cl);
0280: }
0281: }
0282:
0283: return this Class;
0284: }
0285:
0286: private void createClass2(ClassLoader cl) {
0287: CacheKey key = new CacheKey(super Class, interfaces,
0288: methodFilter, handler);
0289: /*
0290: * Excessive concurrency causes a large memory footprint and slows the
0291: * execution speed down (with JDK 1.5). Thus, we use a jumbo lock for
0292: * reducing concrrency.
0293: */
0294: // synchronized (proxyCache) {
0295: HashMap cacheForTheLoader = (HashMap) proxyCache.get(cl);
0296: if (cacheForTheLoader == null) {
0297: cacheForTheLoader = new HashMap();
0298: proxyCache.put(cl, cacheForTheLoader);
0299: cacheForTheLoader.put(key, key);
0300: } else {
0301: CacheKey found = (CacheKey) cacheForTheLoader.get(key);
0302: if (found == null)
0303: cacheForTheLoader.put(key, key);
0304: else {
0305: key = found;
0306: Class c = isValidEntry(key); // no need to synchronize
0307: if (c != null) {
0308: this Class = c;
0309: return;
0310: }
0311: }
0312: }
0313: // }
0314:
0315: // synchronized (key) {
0316: Class c = isValidEntry(key);
0317: if (c == null) {
0318: createClass3(cl);
0319: key.proxyClass = new WeakReference(this Class);
0320: } else
0321: this Class = c;
0322: // }
0323: }
0324:
0325: private Class isValidEntry(CacheKey key) {
0326: WeakReference ref = key.proxyClass;
0327: if (ref != null) {
0328: Class c = (Class) ref.get();
0329: if (c != null)
0330: return c;
0331: }
0332:
0333: return null;
0334: }
0335:
0336: private void createClass3(ClassLoader cl) {
0337: try {
0338: ClassFile cf = make();
0339: if (writeDirectory != null)
0340: FactoryHelper.writeFile(cf, writeDirectory);
0341:
0342: this Class = FactoryHelper.toClass(cf, cl, getDomain());
0343: setField(DEFAULT_INTERCEPTOR, handler);
0344: setField(METHOD_FILTER_FIELD, methodFilter);
0345: } catch (CannotCompileException e) {
0346: throw new RuntimeException(e.getMessage(), e);
0347: }
0348:
0349: }
0350:
0351: private void setField(String fieldName, Object value) {
0352: if (this Class != null && value != null)
0353: try {
0354: Field f = this Class.getField(fieldName);
0355: f.setAccessible(true);
0356: f.set(null, value);
0357: f.setAccessible(false);
0358: } catch (Exception e) {
0359: throw new RuntimeException(e);
0360: }
0361: }
0362:
0363: static MethodFilter getFilter(Class clazz) {
0364: return (MethodFilter) getField(clazz, METHOD_FILTER_FIELD);
0365: }
0366:
0367: static MethodHandler getHandler(Class clazz) {
0368: return (MethodHandler) getField(clazz, DEFAULT_INTERCEPTOR);
0369: }
0370:
0371: private static Object getField(Class clazz, String fieldName) {
0372: try {
0373: Field f = clazz.getField(fieldName);
0374: f.setAccessible(true);
0375: Object value = f.get(null);
0376: f.setAccessible(false);
0377: return value;
0378: } catch (Exception e) {
0379: throw new RuntimeException(e);
0380: }
0381: }
0382:
0383: /**
0384: * A provider of class loaders.
0385: *
0386: * @see #classLoaderProvider
0387: * @since 3.4
0388: */
0389: public static interface ClassLoaderProvider {
0390: /**
0391: * Returns a class loader.
0392: *
0393: * @param pf a proxy factory that is going to obtain a class loader.
0394: */
0395: public ClassLoader get(ProxyFactory pf);
0396: }
0397:
0398: /**
0399: * A provider used by <code>createClass()</code> for obtaining
0400: * a class loader.
0401: * <code>get()</code> on this <code>ClassLoaderProvider</code> object
0402: * is called to obtain a class loader.
0403: *
0404: * <p>The value of this field can be updated for changing the default
0405: * implementation.
0406: *
0407: * <p>Example:
0408: * <ul><pre>
0409: * ProxyFactory.classLoaderProvider = new ProxyFactory.ClassLoaderProvider() {
0410: * public ClassLoader get(ProxyFactory pf) {
0411: * return Thread.currentThread().getContextClassLoader();
0412: * }
0413: * };
0414: * </pre></ul>
0415: *
0416: * @since 3.4
0417: */
0418: public static ClassLoaderProvider classLoaderProvider = new ClassLoaderProvider() {
0419: public ClassLoader get(ProxyFactory pf) {
0420: return pf.getClassLoader0();
0421: }
0422: };
0423:
0424: protected ClassLoader getClassLoader() {
0425: return classLoaderProvider.get(this );
0426: }
0427:
0428: protected ClassLoader getClassLoader0() {
0429: ClassLoader loader = null;
0430: if (super Class != null
0431: && !super Class.getName().equals("java.lang.Object"))
0432: loader = super Class.getClassLoader();
0433: else if (interfaces != null && interfaces.length > 0)
0434: loader = interfaces[0].getClassLoader();
0435:
0436: if (loader == null) {
0437: loader = getClass().getClassLoader();
0438: // In case javassist is in the endorsed dir
0439: if (loader == null) {
0440: loader = Thread.currentThread().getContextClassLoader();
0441: if (loader == null)
0442: loader = ClassLoader.getSystemClassLoader();
0443: }
0444: }
0445:
0446: return loader;
0447: }
0448:
0449: protected ProtectionDomain getDomain() {
0450: Class clazz;
0451: if (super Class != null
0452: && !super Class.getName().equals("java.lang.Object"))
0453: clazz = super Class;
0454: else if (interfaces != null && interfaces.length > 0)
0455: clazz = interfaces[0];
0456: else
0457: clazz = this .getClass();
0458:
0459: return clazz.getProtectionDomain();
0460: }
0461:
0462: /**
0463: * Creates a proxy class and returns an instance of that class.
0464: *
0465: * @param paramTypes parameter types for a constructor.
0466: * @param args arguments passed to a constructor.
0467: * @param mh the method handler for the proxy class.
0468: * @since 3.4
0469: */
0470: public Object create(Class[] paramTypes, Object[] args,
0471: MethodHandler mh) throws NoSuchMethodException,
0472: IllegalArgumentException, InstantiationException,
0473: IllegalAccessException, InvocationTargetException {
0474: Object obj = create(paramTypes, args);
0475: ((ProxyObject) obj).setHandler(mh);
0476: return obj;
0477: }
0478:
0479: /**
0480: * Creates a proxy class and returns an instance of that class.
0481: *
0482: * @param paramTypes parameter types for a constructor.
0483: * @param args arguments passed to a constructor.
0484: */
0485: public Object create(Class[] paramTypes, Object[] args)
0486: throws NoSuchMethodException, IllegalArgumentException,
0487: InstantiationException, IllegalAccessException,
0488: InvocationTargetException {
0489: Class c = createClass();
0490: Constructor cons = c.getConstructor(paramTypes);
0491: return cons.newInstance(args);
0492: }
0493:
0494: /**
0495: * Sets the default invocation handler. This invocation handler is shared
0496: * among all the instances of a proxy class unless another is explicitly
0497: * specified.
0498: */
0499: public void setHandler(MethodHandler mi) {
0500: handler = mi;
0501: setField(DEFAULT_INTERCEPTOR, handler);
0502: }
0503:
0504: private static int counter = 0;
0505:
0506: private static synchronized String makeProxyName(String classname) {
0507: return classname + "_$$_javassist_" + counter++;
0508: }
0509:
0510: private ClassFile make() throws CannotCompileException {
0511: String super Name, classname;
0512: if (interfaces == null)
0513: interfaces = new Class[0];
0514:
0515: if (super Class == null) {
0516: super Class = OBJECT_TYPE;
0517: super Name = super Class.getName();
0518: classname = interfaces.length == 0 ? super Name
0519: : interfaces[0].getName();
0520: } else {
0521: super Name = super Class.getName();
0522: classname = super Name;
0523: }
0524:
0525: if (Modifier.isFinal(super Class.getModifiers()))
0526: throw new CannotCompileException(super Name + " is final");
0527:
0528: classname = makeProxyName(classname);
0529: if (classname.startsWith("java."))
0530: classname = "org.javassist.tmp." + classname;
0531:
0532: ClassFile cf = new ClassFile(false, classname, super Name);
0533: cf.setAccessFlags(AccessFlag.PUBLIC);
0534: setInterfaces(cf, interfaces);
0535: ConstPool pool = cf.getConstPool();
0536: FieldInfo finfo = new FieldInfo(pool, DEFAULT_INTERCEPTOR,
0537: HANDLER_TYPE);
0538: finfo.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
0539: cf.addField(finfo);
0540:
0541: FieldInfo finfo2 = new FieldInfo(pool, HANDLER, HANDLER_TYPE);
0542: finfo2.setAccessFlags(AccessFlag.PRIVATE);
0543: cf.addField(finfo2);
0544:
0545: FieldInfo finfo3 = new FieldInfo(pool, METHOD_FILTER_FIELD,
0546: "Ljavassist/util/proxy/MethodFilter;");
0547: finfo3.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
0548: cf.addField(finfo3);
0549:
0550: HashMap allMethods = getMethods(super Class, interfaces);
0551: int size = allMethods.size();
0552: makeConstructors(classname, cf, pool, classname);
0553: int s = overrideMethods(cf, pool, classname, allMethods);
0554: addMethodsHolder(cf, pool, classname, s);
0555: addSetter(classname, cf, pool);
0556:
0557: try {
0558: cf.addMethod(makeWriteReplace(pool));
0559: } catch (DuplicateMemberException e) {
0560: // writeReplace() is already declared in the super class/interfaces.
0561: }
0562:
0563: this Class = null;
0564: return cf;
0565: }
0566:
0567: private static void setInterfaces(ClassFile cf, Class[] interfaces) {
0568: String setterIntf = ProxyObject.class.getName();
0569: String[] list;
0570: if (interfaces == null || interfaces.length == 0)
0571: list = new String[] { setterIntf };
0572: else {
0573: list = new String[interfaces.length + 1];
0574: for (int i = 0; i < interfaces.length; i++)
0575: list[i] = interfaces[i].getName();
0576:
0577: list[interfaces.length] = setterIntf;
0578: }
0579:
0580: cf.setInterfaces(list);
0581: }
0582:
0583: private static void addMethodsHolder(ClassFile cf, ConstPool cp,
0584: String classname, int size) throws CannotCompileException {
0585: FieldInfo finfo = new FieldInfo(cp, HOLDER, HOLDER_TYPE);
0586: finfo.setAccessFlags(AccessFlag.PRIVATE | AccessFlag.STATIC);
0587: cf.addField(finfo);
0588: MethodInfo minfo = new MethodInfo(cp, "<clinit>", "()V");
0589: Bytecode code = new Bytecode(cp, 0, 0);
0590: code.addIconst(size * 2);
0591: code.addAnewarray("java.lang.reflect.Method");
0592: code.addPutstatic(classname, HOLDER, HOLDER_TYPE);
0593: code.addOpcode(Bytecode.RETURN);
0594: minfo.setCodeAttribute(code.toCodeAttribute());
0595: cf.addMethod(minfo);
0596: }
0597:
0598: private static void addSetter(String classname, ClassFile cf,
0599: ConstPool cp) throws CannotCompileException {
0600: MethodInfo minfo = new MethodInfo(cp, HANDLER_SETTER,
0601: HANDLER_SETTER_TYPE);
0602: minfo.setAccessFlags(AccessFlag.PUBLIC);
0603: Bytecode code = new Bytecode(cp, 2, 2);
0604: code.addAload(0);
0605: code.addAload(1);
0606: code.addPutfield(classname, HANDLER, HANDLER_TYPE);
0607: code.addOpcode(Bytecode.RETURN);
0608: minfo.setCodeAttribute(code.toCodeAttribute());
0609: cf.addMethod(minfo);
0610: }
0611:
0612: private int overrideMethods(ClassFile cf, ConstPool cp,
0613: String className, HashMap allMethods)
0614: throws CannotCompileException {
0615: String prefix = makeUniqueName("_d", allMethods);
0616: Set entries = allMethods.entrySet();
0617: Iterator it = entries.iterator();
0618: int index = 0;
0619: while (it.hasNext()) {
0620: Map.Entry e = (Map.Entry) it.next();
0621: String key = (String) e.getKey();
0622: Method meth = (Method) e.getValue();
0623: int mod = meth.getModifiers();
0624: if (!Modifier.isFinal(mod) && !Modifier.isStatic(mod)
0625: && isVisible(mod, className, meth))
0626: if (methodFilter == null
0627: || methodFilter.isHandled(meth))
0628: override(className, meth, prefix, index++,
0629: keyToDesc(key), cf, cp);
0630: }
0631:
0632: return index;
0633: }
0634:
0635: private void override(String this Classname, Method meth,
0636: String prefix, int index, String desc, ClassFile cf,
0637: ConstPool cp) throws CannotCompileException {
0638: Class declClass = meth.getDeclaringClass();
0639: String delegatorName = prefix + index + meth.getName();
0640: if (Modifier.isAbstract(meth.getModifiers()))
0641: delegatorName = null;
0642: else {
0643: MethodInfo delegator = makeDelegator(meth, desc, cp,
0644: declClass, delegatorName);
0645: // delegator is not a bridge method. See Sec. 15.12.4.5 of JLS 3rd Ed.
0646: delegator.setAccessFlags(delegator.getAccessFlags()
0647: & ~AccessFlag.BRIDGE);
0648: cf.addMethod(delegator);
0649: }
0650:
0651: MethodInfo forwarder = makeForwarder(this Classname, meth, desc,
0652: cp, declClass, delegatorName, index);
0653: cf.addMethod(forwarder);
0654: }
0655:
0656: private void makeConstructors(String this ClassName, ClassFile cf,
0657: ConstPool cp, String classname)
0658: throws CannotCompileException {
0659: Constructor[] cons = super Class.getDeclaredConstructors();
0660: for (int i = 0; i < cons.length; i++) {
0661: Constructor c = cons[i];
0662: int mod = c.getModifiers();
0663: if (!Modifier.isFinal(mod) && !Modifier.isPrivate(mod)
0664: && isVisible(mod, classname, c)) {
0665: MethodInfo m = makeConstructor(this ClassName, c, cp,
0666: super Class);
0667: cf.addMethod(m);
0668: }
0669: }
0670: }
0671:
0672: private static String makeUniqueName(String name, HashMap hash) {
0673: Set keys = hash.keySet();
0674: if (makeUniqueName0(name, keys.iterator()))
0675: return name;
0676:
0677: for (int i = 100; i < 999; i++) {
0678: String s = name + i;
0679: if (makeUniqueName0(s, keys.iterator()))
0680: return s;
0681: }
0682:
0683: throw new RuntimeException("cannot make a unique method name");
0684: }
0685:
0686: private static boolean makeUniqueName0(String name, Iterator it) {
0687: while (it.hasNext()) {
0688: String key = (String) it.next();
0689: if (key.startsWith(name))
0690: return false;
0691: }
0692:
0693: return true;
0694: }
0695:
0696: /**
0697: * Returns true if the method is visible from the class.
0698: *
0699: * @param mod the modifiers of the method.
0700: */
0701: private static boolean isVisible(int mod, String from, Member meth) {
0702: if ((mod & Modifier.PRIVATE) != 0)
0703: return false;
0704: else if ((mod & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0)
0705: return true;
0706: else {
0707: String p = getPackageName(from);
0708: String q = getPackageName(meth.getDeclaringClass()
0709: .getName());
0710: if (p == null)
0711: return q == null;
0712: else
0713: return p.equals(q);
0714: }
0715: }
0716:
0717: private static String getPackageName(String name) {
0718: int i = name.lastIndexOf('.');
0719: if (i < 0)
0720: return null;
0721: else
0722: return name.substring(0, i);
0723: }
0724:
0725: private static HashMap getMethods(Class super Class,
0726: Class[] interfaceTypes) {
0727: HashMap hash = new HashMap();
0728: for (int i = 0; i < interfaceTypes.length; i++)
0729: getMethods(hash, interfaceTypes[i]);
0730:
0731: getMethods(hash, super Class);
0732: return hash;
0733: }
0734:
0735: private static void getMethods(HashMap hash, Class clazz) {
0736: Class[] ifs = clazz.getInterfaces();
0737: for (int i = 0; i < ifs.length; i++)
0738: getMethods(hash, ifs[i]);
0739:
0740: Class parent = clazz.getSuperclass();
0741: if (parent != null)
0742: getMethods(hash, parent);
0743:
0744: Method[] methods = clazz.getDeclaredMethods();
0745: for (int i = 0; i < methods.length; i++)
0746: if (!Modifier.isPrivate(methods[i].getModifiers())) {
0747: Method m = methods[i];
0748: String key = m.getName() + ':'
0749: + RuntimeSupport.makeDescriptor(m);
0750: hash.put(key, methods[i]);
0751: }
0752: }
0753:
0754: private static String keyToDesc(String key) {
0755: return key.substring(key.indexOf(':') + 1);
0756: }
0757:
0758: private static MethodInfo makeConstructor(String this ClassName,
0759: Constructor cons, ConstPool cp, Class super Class) {
0760: String desc = RuntimeSupport.makeDescriptor(cons
0761: .getParameterTypes(), Void.TYPE);
0762: MethodInfo minfo = new MethodInfo(cp, "<init>", desc);
0763: minfo.setAccessFlags(Modifier.PUBLIC); // cons.getModifiers() & ~Modifier.NATIVE
0764: setThrows(minfo, cp, cons.getExceptionTypes());
0765: Bytecode code = new Bytecode(cp, 0, 0);
0766:
0767: code.addAload(0);
0768: code.addGetstatic(this ClassName, DEFAULT_INTERCEPTOR,
0769: HANDLER_TYPE);
0770: code.addOpcode(Opcode.DUP);
0771: code.addOpcode(Opcode.IFNONNULL);
0772: code.addIndex(7);
0773: code.addOpcode(Opcode.POP);
0774: code.addGetstatic(NULL_INTERCEPTOR_HOLDER, DEFAULT_INTERCEPTOR,
0775: HANDLER_TYPE);
0776: code.addPutfield(this ClassName, HANDLER, HANDLER_TYPE);
0777:
0778: code.addAload(0);
0779: int s = addLoadParameters(code, cons.getParameterTypes(), 1);
0780: code.addInvokespecial(super Class.getName(), "<init>", desc);
0781: code.addOpcode(Opcode.RETURN);
0782: code.setMaxLocals(s + 1);
0783: minfo.setCodeAttribute(code.toCodeAttribute());
0784: return minfo;
0785: }
0786:
0787: private static MethodInfo makeDelegator(Method meth, String desc,
0788: ConstPool cp, Class declClass, String delegatorName) {
0789: MethodInfo delegator = new MethodInfo(cp, delegatorName, desc);
0790: delegator.setAccessFlags(Modifier.FINAL
0791: | Modifier.PUBLIC
0792: | (meth.getModifiers() & ~(Modifier.PRIVATE
0793: | Modifier.PROTECTED | Modifier.ABSTRACT
0794: | Modifier.NATIVE | Modifier.SYNCHRONIZED)));
0795: setThrows(delegator, cp, meth);
0796: Bytecode code = new Bytecode(cp, 0, 0);
0797: code.addAload(0);
0798: int s = addLoadParameters(code, meth.getParameterTypes(), 1);
0799: code
0800: .addInvokespecial(declClass.getName(), meth.getName(),
0801: desc);
0802: addReturn(code, meth.getReturnType());
0803: code.setMaxLocals(++s);
0804: delegator.setCodeAttribute(code.toCodeAttribute());
0805: return delegator;
0806: }
0807:
0808: /**
0809: * @param delegatorName null if the original method is abstract.
0810: */
0811: private static MethodInfo makeForwarder(String this ClassName,
0812: Method meth, String desc, ConstPool cp, Class declClass,
0813: String delegatorName, int index) {
0814: MethodInfo forwarder = new MethodInfo(cp, meth.getName(), desc);
0815: forwarder.setAccessFlags(Modifier.FINAL
0816: | (meth.getModifiers() & ~(Modifier.ABSTRACT
0817: | Modifier.NATIVE | Modifier.SYNCHRONIZED)));
0818: setThrows(forwarder, cp, meth);
0819: int args = Descriptor.paramSize(desc);
0820: Bytecode code = new Bytecode(cp, 0, args + 2);
0821: /*
0822: * if (methods[index * 2] == null) {
0823: * methods[index * 2]
0824: * = RuntimeSupport.findMethod(this, <overridden name>, <desc>);
0825: * methods[index * 2 + 1]
0826: * = RuntimeSupport.findMethod(this, <delegator name>, <desc>);
0827: * or = null // the original method is abstract.
0828: * }
0829: * return ($r)handler.invoke(this, methods[index * 2],
0830: * methods[index * 2 + 1], $args);
0831: */
0832: int origIndex = index * 2;
0833: int delIndex = index * 2 + 1;
0834: int arrayVar = args + 1;
0835: code.addGetstatic(this ClassName, HOLDER, HOLDER_TYPE);
0836: code.addAstore(arrayVar);
0837: code.addAload(arrayVar);
0838: code.addIconst(origIndex);
0839: code.addOpcode(Opcode.AALOAD);
0840: code.addOpcode(Opcode.IFNONNULL);
0841: int pc = code.currentPc();
0842: code.addIndex(0);
0843:
0844: callFindMethod(code, "findSuperMethod", arrayVar, origIndex,
0845: meth.getName(), desc);
0846: callFindMethod(code, "findMethod", arrayVar, delIndex,
0847: delegatorName, desc);
0848:
0849: code.write16bit(pc, code.currentPc() - pc + 1);
0850: code.addAload(0);
0851: code.addGetfield(this ClassName, HANDLER, HANDLER_TYPE);
0852: code.addAload(0);
0853:
0854: code.addAload(arrayVar);
0855: code.addIconst(origIndex);
0856: code.addOpcode(Opcode.AALOAD);
0857:
0858: code.addAload(arrayVar);
0859: code.addIconst(delIndex);
0860: code.addOpcode(Opcode.AALOAD);
0861:
0862: makeParameterList(code, meth.getParameterTypes());
0863: code
0864: .addInvokeinterface(
0865: MethodHandler.class.getName(),
0866: "invoke",
0867: "(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;",
0868: 5);
0869: Class retType = meth.getReturnType();
0870: addUnwrapper(code, retType);
0871: addReturn(code, retType);
0872:
0873: forwarder.setCodeAttribute(code.toCodeAttribute());
0874: return forwarder;
0875: }
0876:
0877: private static void setThrows(MethodInfo minfo, ConstPool cp,
0878: Method orig) {
0879: Class[] exceptions = orig.getExceptionTypes();
0880: setThrows(minfo, cp, exceptions);
0881: }
0882:
0883: private static void setThrows(MethodInfo minfo, ConstPool cp,
0884: Class[] exceptions) {
0885: if (exceptions.length == 0)
0886: return;
0887:
0888: String[] list = new String[exceptions.length];
0889: for (int i = 0; i < exceptions.length; i++)
0890: list[i] = exceptions[i].getName();
0891:
0892: ExceptionsAttribute ea = new ExceptionsAttribute(cp);
0893: ea.setExceptions(list);
0894: minfo.setExceptionsAttribute(ea);
0895: }
0896:
0897: private static int addLoadParameters(Bytecode code, Class[] params,
0898: int offset) {
0899: int stacksize = 0;
0900: int n = params.length;
0901: for (int i = 0; i < n; ++i)
0902: stacksize += addLoad(code, stacksize + offset, params[i]);
0903:
0904: return stacksize;
0905: }
0906:
0907: private static int addLoad(Bytecode code, int n, Class type) {
0908: if (type.isPrimitive()) {
0909: if (type == Long.TYPE) {
0910: code.addLload(n);
0911: return 2;
0912: } else if (type == Float.TYPE)
0913: code.addFload(n);
0914: else if (type == Double.TYPE) {
0915: code.addDload(n);
0916: return 2;
0917: } else
0918: code.addIload(n);
0919: } else
0920: code.addAload(n);
0921:
0922: return 1;
0923: }
0924:
0925: private static int addReturn(Bytecode code, Class type) {
0926: if (type.isPrimitive()) {
0927: if (type == Long.TYPE) {
0928: code.addOpcode(Opcode.LRETURN);
0929: return 2;
0930: } else if (type == Float.TYPE)
0931: code.addOpcode(Opcode.FRETURN);
0932: else if (type == Double.TYPE) {
0933: code.addOpcode(Opcode.DRETURN);
0934: return 2;
0935: } else if (type == Void.TYPE) {
0936: code.addOpcode(Opcode.RETURN);
0937: return 0;
0938: } else
0939: code.addOpcode(Opcode.IRETURN);
0940: } else
0941: code.addOpcode(Opcode.ARETURN);
0942:
0943: return 1;
0944: }
0945:
0946: private static void makeParameterList(Bytecode code, Class[] params) {
0947: int regno = 1;
0948: int n = params.length;
0949: code.addIconst(n);
0950: code.addAnewarray("java/lang/Object");
0951: for (int i = 0; i < n; i++) {
0952: code.addOpcode(Opcode.DUP);
0953: code.addIconst(i);
0954: Class type = params[i];
0955: if (type.isPrimitive())
0956: regno = makeWrapper(code, type, regno);
0957: else {
0958: code.addAload(regno);
0959: regno++;
0960: }
0961:
0962: code.addOpcode(Opcode.AASTORE);
0963: }
0964: }
0965:
0966: private static int makeWrapper(Bytecode code, Class type, int regno) {
0967: int index = FactoryHelper.typeIndex(type);
0968: String wrapper = FactoryHelper.wrapperTypes[index];
0969: code.addNew(wrapper);
0970: code.addOpcode(Opcode.DUP);
0971: addLoad(code, regno, type);
0972: code.addInvokespecial(wrapper, "<init>",
0973: FactoryHelper.wrapperDesc[index]);
0974: return regno + FactoryHelper.dataSize[index];
0975: }
0976:
0977: /**
0978: * @param methodName might be null.
0979: */
0980: private static void callFindMethod(Bytecode code,
0981: String findMethod, int arrayVar, int index,
0982: String methodName, String desc) {
0983: String findClass = RuntimeSupport.class.getName();
0984: String findDesc = "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/reflect/Method;";
0985:
0986: code.addAload(arrayVar);
0987: code.addIconst(index);
0988: if (methodName == null)
0989: code.addOpcode(Opcode.ACONST_NULL);
0990: else {
0991: code.addAload(0);
0992: code.addLdc(methodName);
0993: code.addLdc(desc);
0994: code.addInvokestatic(findClass, findMethod, findDesc);
0995: }
0996:
0997: code.addOpcode(Opcode.AASTORE);
0998: }
0999:
1000: private static void addUnwrapper(Bytecode code, Class type) {
1001: if (type.isPrimitive()) {
1002: if (type == Void.TYPE)
1003: code.addOpcode(Opcode.POP);
1004: else {
1005: int index = FactoryHelper.typeIndex(type);
1006: String wrapper = FactoryHelper.wrapperTypes[index];
1007: code.addCheckcast(wrapper);
1008: code.addInvokevirtual(wrapper,
1009: FactoryHelper.unwarpMethods[index],
1010: FactoryHelper.unwrapDesc[index]);
1011: }
1012: } else
1013: code.addCheckcast(type.getName());
1014: }
1015:
1016: private static MethodInfo makeWriteReplace(ConstPool cp) {
1017: MethodInfo minfo = new MethodInfo(cp, "writeReplace",
1018: "()Ljava/lang/Object;");
1019: String[] list = new String[1];
1020: list[0] = "java.io.ObjectStreamException";
1021: ExceptionsAttribute ea = new ExceptionsAttribute(cp);
1022: ea.setExceptions(list);
1023: minfo.setExceptionsAttribute(ea);
1024: Bytecode code = new Bytecode(cp, 0, 1);
1025: code.addAload(0);
1026: code
1027: .addInvokestatic("javassist.util.proxy.RuntimeSupport",
1028: "makeSerializedProxy",
1029: "(Ljava/lang/Object;)Ljavassist/util/proxy/SerializedProxy;");
1030: code.addOpcode(Opcode.ARETURN);
1031: minfo.setCodeAttribute(code.toCodeAttribute());
1032: return minfo;
1033: }
1034: }
|