Home | History | Annotate | Download | only in proxy
      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.util.proxy;
     17 
     18 import java.lang.reflect.Field;
     19 import java.lang.reflect.InvocationTargetException;
     20 import java.lang.reflect.Method;
     21 import java.lang.reflect.Constructor;
     22 import java.lang.reflect.Member;
     23 import java.lang.reflect.Modifier;
     24 import java.security.ProtectionDomain;
     25 import java.util.*;
     26 import java.lang.ref.WeakReference;
     27 
     28 import javassist.CannotCompileException;
     29 import javassist.bytecode.*;
     30 
     31 /*
     32  * This class is implemented only with the lower-level API of Javassist.
     33  * This design decision is for maximizing performance.
     34  */
     35 
     36 /**
     37  * Factory of dynamic proxy classes.
     38  *
     39  * <p>This factory generates a class that extends the given super class and implements
     40  * the given interfaces.  The calls of the methods inherited from the super class are
     41  * forwarded and then <code>invoke()</code> is called on the method handler
     42  * associated with instances of the generated class.  The calls of the methods from
     43  * the interfaces are also forwarded to the method handler.
     44  *
     45  * <p>For example, if the following code is executed,
     46  *
     47  * <ul><pre>
     48  * ProxyFactory f = new ProxyFactory();
     49  * f.setSuperclass(Foo.class);
     50  * f.setFilter(new MethodFilter() {
     51  *     public boolean isHandled(Method m) {
     52  *         // ignore finalize()
     53  *         return !m.getName().equals("finalize");
     54  *     }
     55  * });
     56  * Class c = f.createClass();
     57  * MethodHandler mi = new MethodHandler() {
     58  *     public Object invoke(Object self, Method m, Method proceed,
     59  *                          Object[] args) throws Throwable {
     60  *         System.out.println("Name: " + m.getName());
     61  *         return proceed.invoke(self, args);  // execute the original method.
     62  *     }
     63  * };
     64  * Foo foo = (Foo)c.newInstance();
     65  * ((ProxyObject)foo).setHandler(mi);
     66  * </pre></ul>
     67  *
     68  * <p>Then, the following method call will be forwarded to MethodHandler
     69  * <code>mi</code> and prints a message before executing the originally called method
     70  * <code>bar()</code> in <code>Foo</code>.
     71  *
     72  * <ul><pre>
     73  * foo.bar();
     74  * </pre></ul>
     75  *
     76  * <p>The last three lines of the code shown above can be replaced with a call to
     77  * the helper method <code>create</code>, which generates a proxy class, instantiates
     78  * it, and sets the method handler of the instance:
     79  *
     80  * <ul><pre>
     81  *     :
     82  * Foo foo = (Foo)f.create(new Class[0], new Object[0], mi);
     83  * </pre></ul>
     84  *
     85  * <p>To change the method handler during runtime,
     86  * execute the following code:
     87  *
     88  * <ul><pre>
     89  * MethodHandler mi = ... ;    // alternative handler
     90  * ((ProxyObject)foo).setHandler(mi);
     91  * </pre></ul>
     92  *
     93  * <p> If setHandler is never called for a proxy instance then it will
     94  * employ the default handler which proceeds by invoking the original method.
     95  * The behaviour of the default handler is identical to the following
     96  * handler:
     97  *
     98  * <ul><pre>
     99  * class EmptyHandler implements MethodHandler {
    100  *     public Object invoke(Object self, Method m,
    101  *                          Method proceed, Object[] args) throws Exception {
    102  *         return proceed.invoke(self, args);
    103  *     }
    104  * }
    105  * </pre></ul>
    106  *
    107  * <p>A proxy factory caches and reuses proxy classes by default. It is possible to reset
    108  * this default globally by setting static field {@link ProxyFactory#useCache} to false.
    109  * Caching may also be configured for a specific factory by calling instance method
    110  * {@link ProxyFactory#setUseCache(boolean)}. It is strongly recommended that new clients
    111  * of class ProxyFactory enable caching. Failure to do so may lead to exhaustion of
    112  * the heap memory area used to store classes.
    113  *
    114  * <p>Caching is automatically disabled for any given proxy factory if deprecated instance
    115  * method {@link ProxyFactory#setHandler(MethodHandler)} is called. This method was
    116  * used to specify a default handler which newly created proxy classes should install
    117  * when they create their instances. It is only retained to provide backward compatibility
    118  * with previous releases of javassist. Unfortunately,this legacy behaviour makes caching
    119  * and reuse of proxy classes impossible. The current programming model expects javassist
    120  * clients to set the handler of a proxy instance explicitly by calling method
    121  * {@link ProxyObject#setHandler(MethodHandler)} as shown in the sample code above. New
    122  * clients are strongly recommended to use this model rather than calling
    123  * {@link ProxyFactory#setHandler(MethodHandler)}.
    124  *
    125  * <p>A proxy object generated by <code>ProxyFactory</code> is serializable
    126  * if its super class or any of its interfaces implement <code>java.io.Serializable</code>.
    127  * However, a serialized proxy object may not be compatible with future releases.
    128  * The serialization support should be used for short-term storage or RMI.
    129  *
    130  * <p>For compatibility with older releases serialization of proxy objects is implemented by
    131  * adding a writeReplace method to the proxy class. This allows a proxy to be serialized
    132  * to a conventional {@link java.io.ObjectOutputStream} and deserialized from a corresponding
    133  * {@link java.io.ObjectInputStream}. However this method suffers from several problems, the most
    134  * notable one being that it fails to serialize state inherited from the proxy's superclass.
    135  * <p>
    136  * An alternative method of serializing proxy objects is available which fixes these problems. It
    137  * requires inhibiting generation of the writeReplace method and instead using instances of
    138  * {@link javassist.util.proxy.ProxyObjectOutputStream} and {@link javassist.util.proxy.ProxyObjectInputStream}
    139  * (which are subclasses of {@link java.io.ObjectOutputStream} and  {@link java.io.ObjectInputStream})
    140  * to serialize and deserialize, respectively, the proxy. These streams recognise javassist proxies and ensure
    141  * that they are serialized and deserialized without the need for the proxy class to implement special methods
    142  * such as writeReplace. Generation of the writeReplace method can be disabled globally by setting static field
    143  * {@link ProxyFactory#useWriteReplace} to false. Alternatively, it may be
    144  * configured per factory by calling instance method {@link ProxyFactory#setUseWriteReplace(boolean)}.
    145  *
    146  * @see MethodHandler
    147  * @since 3.1
    148  * @author Muga Nishizawa
    149  * @author Shigeru Chiba
    150  * @author Andrew Dinn
    151  */
    152 public class ProxyFactory {
    153     private Class superClass;
    154     private Class[] interfaces;
    155     private MethodFilter methodFilter;
    156     private MethodHandler handler;  // retained for legacy usage
    157     private List signatureMethods;
    158     private byte[] signature;
    159     private String classname;
    160     private String basename;
    161     private String superName;
    162     private Class thisClass;
    163     /**
    164      * per factory setting initialised from current setting for useCache but able to be reset before each create call
    165      */
    166     private boolean factoryUseCache;
    167     /**
    168      * per factory setting initialised from current setting for useWriteReplace but able to be reset before each create call
    169      */
    170     private boolean factoryWriteReplace;
    171 
    172 
    173     /**
    174      * If the value of this variable is not null, the class file of
    175      * the generated proxy class is written under the directory specified
    176      * by this variable.  For example, if the value is
    177      * <code>"."</code>, then the class file is written under the current
    178      * directory.  This method is for debugging.
    179      *
    180      * <p>The default value is null.
    181      */
    182     public String writeDirectory;
    183 
    184     private static final Class OBJECT_TYPE = Object.class;
    185 
    186     private static final String HOLDER = "_methods_";
    187     private static final String HOLDER_TYPE = "[Ljava/lang/reflect/Method;";
    188     private static final String FILTER_SIGNATURE_FIELD = "_filter_signature";
    189     private static final String FILTER_SIGNATURE_TYPE = "[B";
    190     private static final String HANDLER = "handler";
    191     private static final String NULL_INTERCEPTOR_HOLDER = "javassist.util.proxy.RuntimeSupport";
    192     private static final String DEFAULT_INTERCEPTOR = "default_interceptor";
    193     private static final String HANDLER_TYPE
    194         = 'L' + MethodHandler.class.getName().replace('.', '/') + ';';
    195     private static final String HANDLER_SETTER = "setHandler";
    196     private static final String HANDLER_SETTER_TYPE = "(" + HANDLER_TYPE + ")V";
    197 
    198     private static final String HANDLER_GETTER = "getHandler";
    199     private static final String HANDLER_GETTER_TYPE = "()" + HANDLER_TYPE;
    200 
    201     private static final String SERIAL_VERSION_UID_FIELD = "serialVersionUID";
    202     private static final String SERIAL_VERSION_UID_TYPE = "J";
    203     private static final int SERIAL_VERSION_UID_VALUE = -1;
    204 
    205     /**
    206      * If true, a generated proxy class is cached and it will be reused
    207      * when generating the proxy class with the same properties is requested.
    208      * The default value is true.
    209      *
    210      * Note that this value merely specifies the initial setting employed by any newly created
    211      * proxy factory. The factory setting may be overwritten by calling factory instance method
    212      * {@link #setUseCache(boolean)}
    213      *
    214      * @since 3.4
    215      */
    216     public static volatile boolean useCache = true;
    217 
    218     /**
    219      * If true, a generated proxy class will implement method writeReplace enabling
    220      * serialization of its proxies to a conventional ObjectOutputStream. this (default)
    221      * setting retains the old javassist behaviour which has the advantage that it
    222      * retains compatibility with older  releases and requires no extra work on the part
    223      * of the client performing the serialization. However, it has the disadvantage that
    224      * state inherited from the superclasses of the proxy is lost during serialization.
    225      * if false then serialization/deserialization of the proxy instances will preserve
    226      * all fields. However, serialization must be performed via a {@link ProxyObjectOutputStream}
    227      * and deserialization must be via {@link ProxyObjectInputStream}. Any attempt to serialize
    228      * proxies whose class was created with useWriteReplace set to false via a normal
    229      * {@link java.io.ObjectOutputStream} will fail.
    230      *
    231      * Note that this value merely specifies the initial setting employed by any newly created
    232      * proxy factory. The factory setting may be overwritten by calling factory instance method
    233      * {@link #setUseWriteReplace(boolean)}
    234      *
    235      * @since 3.4
    236      */
    237     public static volatile boolean useWriteReplace = true;
    238 
    239     /*
    240      * methods allowing individual factory settings for factoryUseCache and factoryWriteReplace to be reset
    241      */
    242 
    243     /**
    244      * test whether this factory uses the proxy cache
    245      * @return true if this factory uses the proxy cache otherwise false
    246      */
    247     public boolean isUseCache()
    248     {
    249         return factoryUseCache;
    250     }
    251 
    252     /**
    253      * configure whether this factory should use the proxy cache
    254      * @param useCache true if this factory should use the proxy cache and false if it should not use the cache
    255      * @throws RuntimeException if a default interceptor has been set for the factory
    256      */
    257     public void setUseCache(boolean useCache)
    258     {
    259         // we cannot allow caching to be used if the factory is configured to install a default interceptor
    260         // field into generated classes
    261         if (handler != null && useCache) {
    262             throw new RuntimeException("caching cannot be enabled if the factory default interceptor has been set");
    263         }
    264         factoryUseCache = useCache;
    265     }
    266 
    267     /**
    268      * test whether this factory installs a writeReplace method in created classes
    269      * @return true if this factory installs a writeReplace method in created classes otherwise false
    270      */
    271     public boolean isUseWriteReplace()
    272     {
    273         return factoryWriteReplace;
    274     }
    275 
    276     /**
    277      * configure whether this factory should add a writeReplace method to created classes
    278      * @param useWriteReplace true if this factory should add a writeReplace method to created classes and false if it
    279      * should not add a writeReplace method
    280      */
    281     public void setUseWriteReplace(boolean useWriteReplace)
    282     {
    283         factoryWriteReplace = useWriteReplace;
    284     }
    285 
    286     private static WeakHashMap proxyCache = new WeakHashMap();
    287 
    288     /**
    289      * determine if a class is a javassist proxy class
    290      * @param cl
    291      * @return true if the class is a javassist proxy class otherwise false
    292      */
    293     public static boolean isProxyClass(Class cl)
    294     {
    295         // all proxies implement ProxyObject. nothing else should.
    296         return (ProxyObject.class.isAssignableFrom(cl));
    297     }
    298 
    299     /**
    300      * used to store details of a specific proxy class in the second tier of the proxy cache. this entry
    301      * will be located in a hashmap keyed by the unique identifying name of the proxy class. the hashmap is
    302      * located in a weak hashmap keyed by the classloader common to all proxy classes in the second tier map.
    303      */
    304     static class ProxyDetails {
    305         /**
    306          * the unique signature of any method filter whose behaviour will be met by this class. each bit in
    307          * the byte array is set if the filter redirects the corresponding super or interface method and clear
    308          * if it does not redirect it.
    309          */
    310         byte[] signature;
    311         /**
    312          * a hexadecimal string representation of the signature bit sequence. this string also forms part
    313          * of the proxy class name.
    314          */
    315         WeakReference proxyClass;
    316         /**
    317          * a flag which is true this class employs writeReplace to perform serialization of its instances
    318          * and false if serialization must employ of a ProxyObjectOutputStream and ProxyObjectInputStream
    319          */
    320         boolean isUseWriteReplace;
    321 
    322         ProxyDetails(byte[] signature, Class proxyClass, boolean isUseWriteReplace)
    323         {
    324             this.signature = signature;
    325             this.proxyClass = new WeakReference(proxyClass);
    326             this.isUseWriteReplace = isUseWriteReplace;
    327         }
    328     }
    329 
    330     /**
    331      * Constructs a factory of proxy class.
    332      */
    333     public ProxyFactory() {
    334         superClass = null;
    335         interfaces = null;
    336         methodFilter = null;
    337         handler = null;
    338         signature = null;
    339         signatureMethods = null;
    340         thisClass = null;
    341         writeDirectory = null;
    342         factoryUseCache = useCache;
    343         factoryWriteReplace = useWriteReplace;
    344     }
    345 
    346     /**
    347      * Sets the super class of a proxy class.
    348      */
    349     public void setSuperclass(Class clazz) {
    350         superClass = clazz;
    351         // force recompute of signature
    352         signature = null;
    353     }
    354 
    355     /**
    356      * Obtains the super class set by <code>setSuperclass()</code>.
    357      *
    358      * @since 3.4
    359      */
    360     public Class getSuperclass() { return superClass; }
    361 
    362     /**
    363      * Sets the interfaces of a proxy class.
    364      */
    365     public void setInterfaces(Class[] ifs) {
    366         interfaces = ifs;
    367         // force recompute of signature
    368         signature = null;
    369     }
    370 
    371     /**
    372      * Obtains the interfaces set by <code>setInterfaces</code>.
    373      *
    374      * @since 3.4
    375      */
    376     public Class[] getInterfaces() { return interfaces; }
    377 
    378     /**
    379      * Sets a filter that selects the methods that will be controlled by a handler.
    380      */
    381     public void setFilter(MethodFilter mf) {
    382         methodFilter = mf;
    383         // force recompute of signature
    384         signature = null;
    385     }
    386 
    387     /**
    388      * Generates a proxy class using the current filter.
    389      */
    390     public Class createClass() {
    391         if (signature == null) {
    392             computeSignature(methodFilter);
    393         }
    394         return createClass1();
    395     }
    396 
    397     /**
    398      * Generates a proxy class using the supplied filter.
    399      */
    400     public Class createClass(MethodFilter filter) {
    401         computeSignature(filter);
    402         return createClass1();
    403     }
    404 
    405     /**
    406      * Generates a proxy class with a specific signature.
    407      * access is package local so ProxyObjectInputStream can use this
    408      * @param signature
    409      * @return
    410      */
    411     Class createClass(byte[] signature)
    412     {
    413         installSignature(signature);
    414         return createClass1();
    415     }
    416 
    417     private Class createClass1() {
    418         if (thisClass == null) {
    419             ClassLoader cl = getClassLoader();
    420             synchronized (proxyCache) {
    421                 if (factoryUseCache)
    422                     createClass2(cl);
    423                 else
    424                     createClass3(cl);
    425             }
    426         }
    427 
    428         // don't retain any unwanted references
    429         Class result = thisClass;
    430         thisClass = null;
    431 
    432         return result;
    433     }
    434 
    435     private static char[] hexDigits =
    436             { '0', '1', '2', '3', '4', '5', '6', '7',
    437             '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
    438 
    439     public String getKey(Class superClass, Class[] interfaces, byte[] signature, boolean useWriteReplace)
    440     {
    441         StringBuffer sbuf = new StringBuffer();
    442         if (superClass != null){
    443             sbuf.append(superClass.getName());
    444         }
    445         sbuf.append(":");
    446         for (int i = 0; i < interfaces.length; i++) {
    447             sbuf.append(interfaces[i].getName());
    448             sbuf.append(":");
    449         }
    450         for (int i = 0; i < signature.length; i++) {
    451             byte b = signature[i];
    452             int lo = b & 0xf;
    453             int hi = (b >> 4) & 0xf;
    454             sbuf.append(hexDigits[lo]);
    455             sbuf.append(hexDigits[hi]);
    456         }
    457         if (useWriteReplace) {
    458             sbuf.append(":w");
    459         }
    460 
    461         return sbuf.toString();
    462     }
    463 
    464     private void createClass2(ClassLoader cl) {
    465         String key = getKey(superClass, interfaces, signature, factoryWriteReplace);
    466         /*
    467          * Excessive concurrency causes a large memory footprint and slows the
    468          * execution speed down (with JDK 1.5).  Thus, we use a jumbo lock for
    469          * reducing concrrency.
    470          */
    471         // synchronized (proxyCache) {
    472             HashMap cacheForTheLoader = (HashMap)proxyCache.get(cl);
    473             ProxyDetails details;
    474             if (cacheForTheLoader == null) {
    475                 cacheForTheLoader = new HashMap();
    476                 proxyCache.put(cl, cacheForTheLoader);
    477             }
    478             details = (ProxyDetails)cacheForTheLoader.get(key);
    479             if (details != null) {
    480                 WeakReference reference = details.proxyClass;
    481                 thisClass = (Class)reference.get();
    482                 if (thisClass != null) {
    483                     return;
    484                 }
    485             }
    486             createClass3(cl);
    487             details = new  ProxyDetails(signature, thisClass, factoryWriteReplace);
    488             cacheForTheLoader.put(key, details);
    489         // }
    490     }
    491 
    492     private void createClass3(ClassLoader cl) {
    493         // we need a new class so we need a new class name
    494         allocateClassName();
    495 
    496         try {
    497             ClassFile cf = make();
    498             if (writeDirectory != null)
    499                 FactoryHelper.writeFile(cf, writeDirectory);
    500 
    501             thisClass = FactoryHelper.toClass(cf, cl, getDomain());
    502             setField(FILTER_SIGNATURE_FIELD, signature);
    503             // legacy behaviour : we only set the default interceptor static field if we are not using the cache
    504             if (!factoryUseCache) {
    505                 setField(DEFAULT_INTERCEPTOR, handler);
    506             }
    507         }
    508         catch (CannotCompileException e) {
    509             throw new RuntimeException(e.getMessage(), e);
    510         }
    511 
    512     }
    513 
    514     private void setField(String fieldName, Object value) {
    515         if (thisClass != null && value != null)
    516             try {
    517                 Field f = thisClass.getField(fieldName);
    518                 SecurityActions.setAccessible(f, true);
    519                 f.set(null, value);
    520                 SecurityActions.setAccessible(f, false);
    521             }
    522             catch (Exception e) {
    523                 throw new RuntimeException(e);
    524             }
    525     }
    526 
    527     static byte[] getFilterSignature(Class clazz) {
    528         return (byte[])getField(clazz, FILTER_SIGNATURE_FIELD);
    529     }
    530 
    531     private static Object getField(Class clazz, String fieldName) {
    532         try {
    533             Field f = clazz.getField(fieldName);
    534             f.setAccessible(true);
    535             Object value = f.get(null);
    536             f.setAccessible(false);
    537             return value;
    538         }
    539         catch (Exception e) {
    540             throw new RuntimeException(e);
    541         }
    542     }
    543 
    544     /**
    545      * A provider of class loaders.
    546      *
    547      * @see #classLoaderProvider
    548      * @since 3.4
    549      */
    550     public static interface ClassLoaderProvider {
    551         /**
    552          * Returns a class loader.
    553          *
    554          * @param pf    a proxy factory that is going to obtain a class loader.
    555          */
    556         public ClassLoader get(ProxyFactory pf);
    557     }
    558 
    559     /**
    560      * A provider used by <code>createClass()</code> for obtaining
    561      * a class loader.
    562      * <code>get()</code> on this <code>ClassLoaderProvider</code> object
    563      * is called to obtain a class loader.
    564      *
    565      * <p>The value of this field can be updated for changing the default
    566      * implementation.
    567      *
    568      * <p>Example:
    569      * <ul><pre>
    570      * ProxyFactory.classLoaderProvider = new ProxyFactory.ClassLoaderProvider() {
    571      *     public ClassLoader get(ProxyFactory pf) {
    572      *         return Thread.currentThread().getContextClassLoader();
    573      *     }
    574      * };
    575      * </pre></ul>
    576      *
    577      * @since 3.4
    578      */
    579     public static ClassLoaderProvider classLoaderProvider
    580         = new ClassLoaderProvider() {
    581               public ClassLoader get(ProxyFactory pf) {
    582                   return pf.getClassLoader0();
    583               }
    584           };
    585 
    586     protected ClassLoader getClassLoader() {
    587         return classLoaderProvider.get(this);
    588     }
    589 
    590     protected ClassLoader getClassLoader0() {
    591         ClassLoader loader = null;
    592         if (superClass != null && !superClass.getName().equals("java.lang.Object"))
    593             loader = superClass.getClassLoader();
    594         else if (interfaces != null && interfaces.length > 0)
    595             loader = interfaces[0].getClassLoader();
    596 
    597         if (loader == null) {
    598             loader = getClass().getClassLoader();
    599             // In case javassist is in the endorsed dir
    600             if (loader == null) {
    601                 loader = Thread.currentThread().getContextClassLoader();
    602                 if (loader == null)
    603                     loader = ClassLoader.getSystemClassLoader();
    604             }
    605         }
    606 
    607         return loader;
    608     }
    609 
    610     protected ProtectionDomain getDomain() {
    611         Class clazz;
    612         if (superClass != null && !superClass.getName().equals("java.lang.Object"))
    613             clazz = superClass;
    614         else if (interfaces != null && interfaces.length > 0)
    615             clazz = interfaces[0];
    616         else
    617             clazz = this.getClass();
    618 
    619         return clazz.getProtectionDomain();
    620     }
    621 
    622     /**
    623      * Creates a proxy class and returns an instance of that class.
    624      *
    625      * @param paramTypes    parameter types for a constructor.
    626      * @param args          arguments passed to a constructor.
    627      * @param mh            the method handler for the proxy class.
    628      * @since 3.4
    629      */
    630     public Object create(Class[] paramTypes, Object[] args, MethodHandler mh)
    631         throws NoSuchMethodException, IllegalArgumentException,
    632                InstantiationException, IllegalAccessException, InvocationTargetException
    633     {
    634         Object obj = create(paramTypes, args);
    635         ((ProxyObject)obj).setHandler(mh);
    636         return obj;
    637     }
    638 
    639     /**
    640      * Creates a proxy class and returns an instance of that class.
    641      *
    642      * @param paramTypes    parameter types for a constructor.
    643      * @param args          arguments passed to a constructor.
    644      */
    645     public Object create(Class[] paramTypes, Object[] args)
    646         throws NoSuchMethodException, IllegalArgumentException,
    647                InstantiationException, IllegalAccessException, InvocationTargetException
    648     {
    649         Class c = createClass();
    650         Constructor cons = c.getConstructor(paramTypes);
    651         return cons.newInstance(args);
    652     }
    653 
    654     /**
    655      * Sets the default invocation handler.  This invocation handler is shared
    656      * among all the instances of a proxy class unless another is explicitly
    657      * specified.
    658      * @deprecated since 3.12
    659      * use of this method is incompatible  with proxy class caching.
    660      * instead clients should call method {@link ProxyObject#setHandler(MethodHandler)} to set the handler
    661      * for each newly created  proxy instance.
    662      * calling this method will automatically disable caching of classes created by the proxy factory.
    663      */
    664     public void setHandler(MethodHandler mi) {
    665         // if we were using the cache and the handler is non-null then we must stop caching
    666         if (factoryUseCache && mi != null)  {
    667             factoryUseCache = false;
    668             // clear any currently held class so we don't try to reuse it or set its handler field
    669           thisClass  = null;
    670         }
    671         handler = mi;
    672         // this retains the behaviour of the old code which resets any class we were holding on to
    673         // this is probably not what is wanted
    674         setField(DEFAULT_INTERCEPTOR, handler);
    675     }
    676 
    677     private static int counter = 0;
    678 
    679     private static synchronized String makeProxyName(String classname) {
    680         return classname + "_$$_javassist_" + counter++;
    681     }
    682 
    683     private ClassFile make() throws CannotCompileException {
    684         ClassFile cf = new ClassFile(false, classname, superName);
    685         cf.setAccessFlags(AccessFlag.PUBLIC);
    686         setInterfaces(cf, interfaces);
    687         ConstPool pool = cf.getConstPool();
    688 
    689         // legacy: we only add the static field for the default interceptor if caching is disabled
    690         if  (!factoryUseCache) {
    691             FieldInfo finfo = new FieldInfo(pool, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
    692             finfo.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
    693             cf.addField(finfo);
    694         }
    695 
    696         // handler is per instance
    697         FieldInfo finfo2 = new FieldInfo(pool, HANDLER, HANDLER_TYPE);
    698         finfo2.setAccessFlags(AccessFlag.PRIVATE);
    699         cf.addField(finfo2);
    700 
    701         // filter signature is per class
    702         FieldInfo finfo3 = new FieldInfo(pool, FILTER_SIGNATURE_FIELD, FILTER_SIGNATURE_TYPE);
    703         finfo3.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
    704         cf.addField(finfo3);
    705 
    706         // the proxy class serial uid must always be a fixed value
    707         FieldInfo finfo4 = new FieldInfo(pool, SERIAL_VERSION_UID_FIELD, SERIAL_VERSION_UID_TYPE);
    708         finfo4.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC| AccessFlag.FINAL);
    709         cf.addField(finfo4);
    710 
    711         // HashMap allMethods = getMethods(superClass, interfaces);
    712         // int size = allMethods.size();
    713         makeConstructors(classname, cf, pool, classname);
    714         int s = overrideMethods(cf, pool, classname);
    715         addMethodsHolder(cf, pool, classname, s);
    716         addSetter(classname, cf, pool);
    717         addGetter(classname, cf, pool);
    718 
    719         if (factoryWriteReplace) {
    720             try {
    721                 cf.addMethod(makeWriteReplace(pool));
    722             }
    723             catch (DuplicateMemberException e) {
    724                 // writeReplace() is already declared in the super class/interfaces.
    725             }
    726         }
    727 
    728         thisClass = null;
    729         return cf;
    730     }
    731 
    732     private void checkClassAndSuperName()
    733     {
    734         if (interfaces == null)
    735             interfaces = new Class[0];
    736 
    737         if (superClass == null) {
    738             superClass = OBJECT_TYPE;
    739             superName = superClass.getName();
    740             basename = interfaces.length == 0 ? superName
    741                                                : interfaces[0].getName();
    742         } else {
    743             superName = superClass.getName();
    744             basename = superName;
    745         }
    746 
    747         if (Modifier.isFinal(superClass.getModifiers()))
    748             throw new RuntimeException(superName + " is final");
    749 
    750         if (basename.startsWith("java."))
    751             basename = "org.javassist.tmp." + basename;
    752     }
    753 
    754     private void allocateClassName()
    755     {
    756         classname = makeProxyName(basename);
    757     }
    758 
    759     private static Comparator sorter = new Comparator() {
    760 
    761         public int compare(Object o1, Object o2) {
    762             Map.Entry e1 = (Map.Entry)o1;
    763             Map.Entry e2 = (Map.Entry)o2;
    764             String key1 = (String)e1.getKey();
    765             String key2 = (String)e2.getKey();
    766             return key1.compareTo(key2);
    767         }
    768     };
    769 
    770     private void makeSortedMethodList()
    771     {
    772         checkClassAndSuperName();
    773 
    774         HashMap allMethods = getMethods(superClass, interfaces);
    775         signatureMethods = new ArrayList(allMethods.entrySet());
    776         Collections.sort(signatureMethods, sorter);
    777     }
    778 
    779     private void computeSignature(MethodFilter filter) // throws CannotCompileException
    780     {
    781         makeSortedMethodList();
    782 
    783         int l = signatureMethods.size();
    784         int maxBytes = ((l + 7) >> 3);
    785         signature = new byte[maxBytes];
    786         for (int idx = 0; idx < l; idx++)
    787         {
    788             Map.Entry e = (Map.Entry)signatureMethods.get(idx);
    789             Method m = (Method)e.getValue();
    790             int mod = m.getModifiers();
    791             if (!Modifier.isFinal(mod) && !Modifier.isStatic(mod)
    792                     && isVisible(mod, basename, m) && (filter == null || filter.isHandled(m))) {
    793                 setBit(signature, idx);
    794             }
    795         }
    796     }
    797 
    798     private void installSignature(byte[] signature) // throws CannotCompileException
    799     {
    800         makeSortedMethodList();
    801 
    802         int l = signatureMethods.size();
    803         int maxBytes = ((l + 7) >> 3);
    804         if (signature.length != maxBytes) {
    805             throw new RuntimeException("invalid filter signature length for deserialized proxy class");
    806         }
    807 
    808         this.signature =  signature;
    809     }
    810 
    811     private boolean testBit(byte[] signature, int idx)
    812     {
    813         int byteIdx = idx >> 3;
    814         if (byteIdx > signature.length) {
    815             return false;
    816         } else {
    817             int bitIdx = idx & 0x7;
    818             int mask = 0x1 << bitIdx;
    819             int sigByte = signature[byteIdx];
    820             return ((sigByte & mask) != 0);
    821         }
    822     }
    823 
    824     private void setBit(byte[] signature, int idx)
    825     {
    826         int byteIdx = idx >> 3;
    827         if (byteIdx < signature.length) {
    828             int bitIdx = idx & 0x7;
    829             int mask = 0x1 << bitIdx;
    830             int sigByte = signature[byteIdx];
    831             signature[byteIdx] = (byte)(sigByte | mask);
    832         }
    833     }
    834 
    835     private static void setInterfaces(ClassFile cf, Class[] interfaces) {
    836         String setterIntf = ProxyObject.class.getName();
    837         String[] list;
    838         if (interfaces == null || interfaces.length == 0)
    839             list = new String[] { setterIntf };
    840         else {
    841             list = new String[interfaces.length + 1];
    842             for (int i = 0; i < interfaces.length; i++)
    843                 list[i] = interfaces[i].getName();
    844 
    845             list[interfaces.length] = setterIntf;
    846         }
    847 
    848         cf.setInterfaces(list);
    849     }
    850 
    851     private static void addMethodsHolder(ClassFile cf, ConstPool cp,
    852                                          String classname, int size)
    853         throws CannotCompileException
    854     {
    855         FieldInfo finfo = new FieldInfo(cp, HOLDER, HOLDER_TYPE);
    856         finfo.setAccessFlags(AccessFlag.PRIVATE | AccessFlag.STATIC);
    857         cf.addField(finfo);
    858         MethodInfo minfo = new MethodInfo(cp, "<clinit>", "()V");
    859         minfo.setAccessFlags(AccessFlag.STATIC);
    860         Bytecode code = new Bytecode(cp, 0, 0);
    861         code.addIconst(size * 2);
    862         code.addAnewarray("java.lang.reflect.Method");
    863         code.addPutstatic(classname, HOLDER, HOLDER_TYPE);
    864         // also need to set serial version uid
    865         code.addLconst(-1L);
    866         code.addPutstatic(classname, SERIAL_VERSION_UID_FIELD, SERIAL_VERSION_UID_TYPE);
    867         code.addOpcode(Bytecode.RETURN);
    868         minfo.setCodeAttribute(code.toCodeAttribute());
    869         cf.addMethod(minfo);
    870     }
    871 
    872     private static void addSetter(String classname, ClassFile cf, ConstPool cp)
    873         throws CannotCompileException
    874     {
    875         MethodInfo minfo = new MethodInfo(cp, HANDLER_SETTER,
    876                                           HANDLER_SETTER_TYPE);
    877         minfo.setAccessFlags(AccessFlag.PUBLIC);
    878         Bytecode code = new Bytecode(cp, 2, 2);
    879         code.addAload(0);
    880         code.addAload(1);
    881         code.addPutfield(classname, HANDLER, HANDLER_TYPE);
    882         code.addOpcode(Bytecode.RETURN);
    883         minfo.setCodeAttribute(code.toCodeAttribute());
    884         cf.addMethod(minfo);
    885     }
    886 
    887     private static void addGetter(String classname, ClassFile cf, ConstPool cp)
    888         throws CannotCompileException
    889     {
    890         MethodInfo minfo = new MethodInfo(cp, HANDLER_GETTER,
    891                                           HANDLER_GETTER_TYPE);
    892         minfo.setAccessFlags(AccessFlag.PUBLIC);
    893         Bytecode code = new Bytecode(cp, 1, 1);
    894         code.addAload(0);
    895         code.addGetfield(classname, HANDLER, HANDLER_TYPE);
    896         code.addOpcode(Bytecode.ARETURN);
    897         minfo.setCodeAttribute(code.toCodeAttribute());
    898         cf.addMethod(minfo);
    899     }
    900 
    901     private int overrideMethods(ClassFile cf, ConstPool cp, String className)
    902         throws CannotCompileException
    903     {
    904         String prefix = makeUniqueName("_d", signatureMethods);
    905         Iterator it = signatureMethods.iterator();
    906         int index = 0;
    907         while (it.hasNext()) {
    908             Map.Entry e = (Map.Entry)it.next();
    909             String key = (String)e.getKey();
    910             Method meth = (Method)e.getValue();
    911             int mod = meth.getModifiers();
    912             if (testBit(signature, index)) {
    913                 override(className, meth, prefix, index,
    914                         keyToDesc(key), cf, cp);
    915             }
    916             index++;
    917         }
    918 
    919         return index;
    920     }
    921 
    922     private void override(String thisClassname, Method meth, String prefix,
    923                           int index, String desc, ClassFile cf, ConstPool cp)
    924         throws CannotCompileException
    925     {
    926         Class declClass = meth.getDeclaringClass();
    927         String delegatorName = prefix + index + meth.getName();
    928         if (Modifier.isAbstract(meth.getModifiers()))
    929             delegatorName = null;
    930         else {
    931             MethodInfo delegator
    932                 = makeDelegator(meth, desc, cp, declClass, delegatorName);
    933             // delegator is not a bridge method.  See Sec. 15.12.4.5 of JLS 3rd Ed.
    934             delegator.setAccessFlags(delegator.getAccessFlags() & ~AccessFlag.BRIDGE);
    935             cf.addMethod(delegator);
    936         }
    937 
    938         MethodInfo forwarder
    939             = makeForwarder(thisClassname, meth, desc, cp, declClass,
    940                             delegatorName, index);
    941         cf.addMethod(forwarder);
    942     }
    943 
    944     private void makeConstructors(String thisClassName, ClassFile cf,
    945             ConstPool cp, String classname) throws CannotCompileException
    946     {
    947         Constructor[] cons = SecurityActions.getDeclaredConstructors(superClass);
    948         // legacy: if we are not caching then we need to initialise the default handler
    949         boolean doHandlerInit = !factoryUseCache;
    950         for (int i = 0; i < cons.length; i++) {
    951             Constructor c = cons[i];
    952             int mod = c.getModifiers();
    953             if (!Modifier.isFinal(mod) && !Modifier.isPrivate(mod)
    954                     && isVisible(mod, basename, c)) {
    955                 MethodInfo m = makeConstructor(thisClassName, c, cp, superClass, doHandlerInit);
    956                 cf.addMethod(m);
    957             }
    958         }
    959     }
    960 
    961     private static String makeUniqueName(String name, List sortedMethods) {
    962         if (makeUniqueName0(name, sortedMethods.iterator()))
    963             return name;
    964 
    965         for (int i = 100; i < 999; i++) {
    966             String s = name + i;
    967             if (makeUniqueName0(s, sortedMethods.iterator()))
    968                 return s;
    969         }
    970 
    971         throw new RuntimeException("cannot make a unique method name");
    972     }
    973 
    974     private static boolean makeUniqueName0(String name, Iterator it) {
    975         while (it.hasNext()) {
    976             Map.Entry e = (Map.Entry)it.next();
    977             String key = (String)e.getKey();
    978             if (key.startsWith(name))
    979                 return false;
    980         }
    981 
    982         return true;
    983     }
    984 
    985     /**
    986      * Returns true if the method is visible from the package.
    987      *
    988      * @param mod       the modifiers of the method.
    989      */
    990     private static boolean isVisible(int mod, String from, Member meth) {
    991         if ((mod & Modifier.PRIVATE) != 0)
    992             return false;
    993         else if ((mod & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0)
    994             return true;
    995         else {
    996             String p = getPackageName(from);
    997             String q = getPackageName(meth.getDeclaringClass().getName());
    998             if (p == null)
    999                 return q == null;
   1000             else
   1001                 return p.equals(q);
   1002         }
   1003     }
   1004 
   1005     private static String getPackageName(String name) {
   1006         int i = name.lastIndexOf('.');
   1007         if (i < 0)
   1008             return null;
   1009         else
   1010             return name.substring(0, i);
   1011     }
   1012 
   1013     private static HashMap getMethods(Class superClass, Class[] interfaceTypes) {
   1014         HashMap hash = new HashMap();
   1015         for (int i = 0; i < interfaceTypes.length; i++)
   1016             getMethods(hash, interfaceTypes[i]);
   1017 
   1018         getMethods(hash, superClass);
   1019         return hash;
   1020     }
   1021 
   1022     private static void getMethods(HashMap hash, Class clazz) {
   1023         Class[] ifs = clazz.getInterfaces();
   1024         for (int i = 0; i < ifs.length; i++)
   1025             getMethods(hash, ifs[i]);
   1026 
   1027         Class parent = clazz.getSuperclass();
   1028         if (parent != null)
   1029             getMethods(hash, parent);
   1030 
   1031         Method[] methods = SecurityActions.getDeclaredMethods(clazz);
   1032         for (int i = 0; i < methods.length; i++)
   1033             if (!Modifier.isPrivate(methods[i].getModifiers())) {
   1034                 Method m = methods[i];
   1035                 String key = m.getName() + ':' + RuntimeSupport.makeDescriptor(m);
   1036                 // JIRA JASSIST-85
   1037                 // put the method to the cache, retrieve previous definition (if any)
   1038                 Method oldMethod = (Method)hash.put(key, methods[i]);
   1039 
   1040                 // check if visibility has been reduced
   1041                 if (null != oldMethod && Modifier.isPublic(oldMethod.getModifiers())
   1042                                       && !Modifier.isPublic(methods[i].getModifiers()) ) {
   1043                     // we tried to overwrite a public definition with a non-public definition,
   1044                     // use the old definition instead.
   1045                     hash.put(key, oldMethod);
   1046                 }
   1047             }
   1048     }
   1049 
   1050     private static String keyToDesc(String key) {
   1051         return key.substring(key.indexOf(':') + 1);
   1052     }
   1053 
   1054     private static MethodInfo makeConstructor(String thisClassName, Constructor cons,
   1055                                               ConstPool cp, Class superClass, boolean doHandlerInit) {
   1056         String desc = RuntimeSupport.makeDescriptor(cons.getParameterTypes(),
   1057                                                     Void.TYPE);
   1058         MethodInfo minfo = new MethodInfo(cp, "<init>", desc);
   1059         minfo.setAccessFlags(Modifier.PUBLIC);      // cons.getModifiers() & ~Modifier.NATIVE
   1060         setThrows(minfo, cp, cons.getExceptionTypes());
   1061         Bytecode code = new Bytecode(cp, 0, 0);
   1062 
   1063         // legacy: if we are not using caching then we initialise the instance's handler
   1064         // from the class's static default interceptor and skip the next few instructions if
   1065         // it is non-null
   1066         if (doHandlerInit) {
   1067             code.addAload(0);
   1068             code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
   1069             code.addPutfield(thisClassName, HANDLER, HANDLER_TYPE);
   1070             code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
   1071             code.addOpcode(Opcode.IFNONNULL);
   1072             code.addIndex(10);
   1073         }
   1074         // if caching is enabled then we don't have a handler to initialise so this else branch will install
   1075         // the handler located in the static field of class RuntimeSupport.
   1076         code.addAload(0);
   1077         code.addGetstatic(NULL_INTERCEPTOR_HOLDER, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
   1078         code.addPutfield(thisClassName, HANDLER, HANDLER_TYPE);
   1079         int pc = code.currentPc();
   1080 
   1081         code.addAload(0);
   1082         int s = addLoadParameters(code, cons.getParameterTypes(), 1);
   1083         code.addInvokespecial(superClass.getName(), "<init>", desc);
   1084         code.addOpcode(Opcode.RETURN);
   1085         code.setMaxLocals(s + 1);
   1086         CodeAttribute ca = code.toCodeAttribute();
   1087         minfo.setCodeAttribute(ca);
   1088 
   1089         StackMapTable.Writer writer = new StackMapTable.Writer(32);
   1090         writer.sameFrame(pc);
   1091         ca.setAttribute(writer.toStackMapTable(cp));
   1092         return minfo;
   1093     }
   1094 
   1095     private static MethodInfo makeDelegator(Method meth, String desc,
   1096                 ConstPool cp, Class declClass, String delegatorName) {
   1097         MethodInfo delegator = new MethodInfo(cp, delegatorName, desc);
   1098         delegator.setAccessFlags(Modifier.FINAL | Modifier.PUBLIC
   1099                 | (meth.getModifiers() & ~(Modifier.PRIVATE
   1100                                            | Modifier.PROTECTED
   1101                                            | Modifier.ABSTRACT
   1102                                            | Modifier.NATIVE
   1103                                            | Modifier.SYNCHRONIZED)));
   1104         setThrows(delegator, cp, meth);
   1105         Bytecode code = new Bytecode(cp, 0, 0);
   1106         code.addAload(0);
   1107         int s = addLoadParameters(code, meth.getParameterTypes(), 1);
   1108         code.addInvokespecial(declClass.getName(), meth.getName(), desc);
   1109         addReturn(code, meth.getReturnType());
   1110         code.setMaxLocals(++s);
   1111         delegator.setCodeAttribute(code.toCodeAttribute());
   1112         return delegator;
   1113     }
   1114 
   1115     /**
   1116      * @param delegatorName     null if the original method is abstract.
   1117      */
   1118     private static MethodInfo makeForwarder(String thisClassName,
   1119                     Method meth, String desc, ConstPool cp,
   1120                     Class declClass, String delegatorName, int index) {
   1121         MethodInfo forwarder = new MethodInfo(cp, meth.getName(), desc);
   1122         forwarder.setAccessFlags(Modifier.FINAL
   1123                     | (meth.getModifiers() & ~(Modifier.ABSTRACT
   1124                                                | Modifier.NATIVE
   1125                                                | Modifier.SYNCHRONIZED)));
   1126         setThrows(forwarder, cp, meth);
   1127         int args = Descriptor.paramSize(desc);
   1128         Bytecode code = new Bytecode(cp, 0, args + 2);
   1129         /*
   1130          * if (methods[index * 2] == null) {
   1131          *   methods[index * 2]
   1132          *     = RuntimeSupport.findSuperMethod(this, <overridden name>, <desc>);
   1133          *   methods[index * 2 + 1]
   1134          *     = RuntimeSupport.findMethod(this, <delegator name>, <desc>);
   1135          *     or = null // the original method is abstract.
   1136          * }
   1137          * return ($r)handler.invoke(this, methods[index * 2],
   1138          *                methods[index * 2 + 1], $args);
   1139          */
   1140         int origIndex = index * 2;
   1141         int delIndex = index * 2 + 1;
   1142         int arrayVar = args + 1;
   1143         code.addGetstatic(thisClassName, HOLDER, HOLDER_TYPE);
   1144         code.addAstore(arrayVar);
   1145 
   1146         callFind2Methods(code, meth.getName(), delegatorName, origIndex, desc, arrayVar);
   1147 
   1148         code.addAload(0);
   1149         code.addGetfield(thisClassName, HANDLER, HANDLER_TYPE);
   1150         code.addAload(0);
   1151 
   1152         code.addAload(arrayVar);
   1153         code.addIconst(origIndex);
   1154         code.addOpcode(Opcode.AALOAD);
   1155 
   1156         code.addAload(arrayVar);
   1157         code.addIconst(delIndex);
   1158         code.addOpcode(Opcode.AALOAD);
   1159 
   1160         makeParameterList(code, meth.getParameterTypes());
   1161         code.addInvokeinterface(MethodHandler.class.getName(), "invoke",
   1162             "(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;",
   1163             5);
   1164         Class retType = meth.getReturnType();
   1165         addUnwrapper(code, retType);
   1166         addReturn(code, retType);
   1167 
   1168         CodeAttribute ca = code.toCodeAttribute();
   1169         forwarder.setCodeAttribute(ca);
   1170         return forwarder;
   1171     }
   1172 
   1173     private static void setThrows(MethodInfo minfo, ConstPool cp, Method orig) {
   1174         Class[] exceptions = orig.getExceptionTypes();
   1175         setThrows(minfo, cp, exceptions);
   1176     }
   1177 
   1178     private static void setThrows(MethodInfo minfo, ConstPool cp,
   1179                                   Class[] exceptions) {
   1180         if (exceptions.length == 0)
   1181             return;
   1182 
   1183         String[] list = new String[exceptions.length];
   1184         for (int i = 0; i < exceptions.length; i++)
   1185             list[i] = exceptions[i].getName();
   1186 
   1187         ExceptionsAttribute ea = new ExceptionsAttribute(cp);
   1188         ea.setExceptions(list);
   1189         minfo.setExceptionsAttribute(ea);
   1190     }
   1191 
   1192     private static int addLoadParameters(Bytecode code, Class[] params,
   1193                                          int offset) {
   1194         int stacksize = 0;
   1195         int n = params.length;
   1196         for (int i = 0; i < n; ++i)
   1197             stacksize += addLoad(code, stacksize + offset, params[i]);
   1198 
   1199         return stacksize;
   1200     }
   1201 
   1202     private static int addLoad(Bytecode code, int n, Class type) {
   1203         if (type.isPrimitive()) {
   1204             if (type == Long.TYPE) {
   1205                 code.addLload(n);
   1206                 return 2;
   1207             }
   1208             else if (type == Float.TYPE)
   1209                 code.addFload(n);
   1210             else if (type == Double.TYPE) {
   1211                 code.addDload(n);
   1212                 return 2;
   1213             }
   1214             else
   1215                 code.addIload(n);
   1216         }
   1217         else
   1218             code.addAload(n);
   1219 
   1220         return 1;
   1221     }
   1222 
   1223     private static int addReturn(Bytecode code, Class type) {
   1224         if (type.isPrimitive()) {
   1225             if (type == Long.TYPE) {
   1226                 code.addOpcode(Opcode.LRETURN);
   1227                 return 2;
   1228             }
   1229             else if (type == Float.TYPE)
   1230                 code.addOpcode(Opcode.FRETURN);
   1231             else if (type == Double.TYPE) {
   1232                 code.addOpcode(Opcode.DRETURN);
   1233                 return 2;
   1234             }
   1235             else if (type == Void.TYPE) {
   1236                 code.addOpcode(Opcode.RETURN);
   1237                 return 0;
   1238             }
   1239             else
   1240                 code.addOpcode(Opcode.IRETURN);
   1241         }
   1242         else
   1243             code.addOpcode(Opcode.ARETURN);
   1244 
   1245         return 1;
   1246     }
   1247 
   1248     private static void makeParameterList(Bytecode code, Class[] params) {
   1249         int regno = 1;
   1250         int n = params.length;
   1251         code.addIconst(n);
   1252         code.addAnewarray("java/lang/Object");
   1253         for (int i = 0; i < n; i++) {
   1254             code.addOpcode(Opcode.DUP);
   1255             code.addIconst(i);
   1256             Class type = params[i];
   1257             if (type.isPrimitive())
   1258                 regno = makeWrapper(code, type, regno);
   1259             else {
   1260                 code.addAload(regno);
   1261                 regno++;
   1262             }
   1263 
   1264             code.addOpcode(Opcode.AASTORE);
   1265         }
   1266     }
   1267 
   1268     private static int makeWrapper(Bytecode code, Class type, int regno) {
   1269         int index = FactoryHelper.typeIndex(type);
   1270         String wrapper = FactoryHelper.wrapperTypes[index];
   1271         code.addNew(wrapper);
   1272         code.addOpcode(Opcode.DUP);
   1273         addLoad(code, regno, type);
   1274         code.addInvokespecial(wrapper, "<init>",
   1275                               FactoryHelper.wrapperDesc[index]);
   1276         return regno + FactoryHelper.dataSize[index];
   1277     }
   1278 
   1279     /**
   1280      * @param thisMethod        might be null.
   1281      */
   1282     private static void callFind2Methods(Bytecode code, String superMethod, String thisMethod,
   1283                                          int index, String desc, int arrayVar) {
   1284         String findClass = RuntimeSupport.class.getName();
   1285         String findDesc
   1286             = "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;[Ljava/lang/reflect/Method;)V";
   1287 
   1288         code.addAload(0);
   1289         code.addLdc(superMethod);
   1290         if (thisMethod == null)
   1291             code.addOpcode(Opcode.ACONST_NULL);
   1292         else
   1293             code.addLdc(thisMethod);
   1294 
   1295         code.addIconst(index);
   1296         code.addLdc(desc);
   1297         code.addAload(arrayVar);
   1298         code.addInvokestatic(findClass, "find2Methods", findDesc);
   1299     }
   1300 
   1301     private static void addUnwrapper(Bytecode code, Class type) {
   1302         if (type.isPrimitive()) {
   1303             if (type == Void.TYPE)
   1304                 code.addOpcode(Opcode.POP);
   1305             else {
   1306                 int index = FactoryHelper.typeIndex(type);
   1307                 String wrapper = FactoryHelper.wrapperTypes[index];
   1308                 code.addCheckcast(wrapper);
   1309                 code.addInvokevirtual(wrapper,
   1310                                       FactoryHelper.unwarpMethods[index],
   1311                                       FactoryHelper.unwrapDesc[index]);
   1312             }
   1313         }
   1314         else
   1315             code.addCheckcast(type.getName());
   1316     }
   1317 
   1318     private static MethodInfo makeWriteReplace(ConstPool cp) {
   1319         MethodInfo minfo = new MethodInfo(cp, "writeReplace", "()Ljava/lang/Object;");
   1320         String[] list = new String[1];
   1321         list[0] = "java.io.ObjectStreamException";
   1322         ExceptionsAttribute ea = new ExceptionsAttribute(cp);
   1323         ea.setExceptions(list);
   1324         minfo.setExceptionsAttribute(ea);
   1325         Bytecode code = new Bytecode(cp, 0, 1);
   1326         code.addAload(0);
   1327         code.addInvokestatic("javassist.util.proxy.RuntimeSupport",
   1328                              "makeSerializedProxy",
   1329                              "(Ljava/lang/Object;)Ljavassist/util/proxy/SerializedProxy;");
   1330         code.addOpcode(Opcode.ARETURN);
   1331         minfo.setCodeAttribute(code.toCodeAttribute());
   1332         return minfo;
   1333     }
   1334 }
   1335