• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
isUseCache()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      */
setUseCache(boolean useCache)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      */
isUseWriteReplace()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      */
setUseWriteReplace(boolean useWriteReplace)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      */
isProxyClass(Class cl)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 
ProxyDetails(byte[] signature, Class proxyClass, boolean isUseWriteReplace)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      */
ProxyFactory()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      */
setSuperclass(Class clazz)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      */
getSuperclass()360     public Class getSuperclass() { return superClass; }
361 
362     /**
363      * Sets the interfaces of a proxy class.
364      */
setInterfaces(Class[] ifs)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      */
getInterfaces()376     public Class[] getInterfaces() { return interfaces; }
377 
378     /**
379      * Sets a filter that selects the methods that will be controlled by a handler.
380      */
setFilter(MethodFilter mf)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      */
createClass()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      */
createClass(MethodFilter filter)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      */
createClass(byte[] signature)411     Class createClass(byte[] signature)
412     {
413         installSignature(signature);
414         return createClass1();
415     }
416 
createClass1()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 
getKey(Class superClass, Class[] interfaces, byte[] signature, boolean useWriteReplace)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 
createClass2(ClassLoader cl)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 
createClass3(ClassLoader cl)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 
setField(String fieldName, Object value)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 
getFilterSignature(Class clazz)527     static byte[] getFilterSignature(Class clazz) {
528         return (byte[])getField(clazz, FILTER_SIGNATURE_FIELD);
529     }
530 
getField(Class clazz, String fieldName)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          */
get(ProxyFactory pf)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 
getClassLoader()586     protected ClassLoader getClassLoader() {
587         return classLoaderProvider.get(this);
588     }
589 
getClassLoader0()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 
getDomain()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      */
create(Class[] paramTypes, Object[] args, MethodHandler mh)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      */
create(Class[] paramTypes, Object[] args)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      */
setHandler(MethodHandler mi)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 
makeProxyName(String classname)679     private static synchronized String makeProxyName(String classname) {
680         return classname + "_$$_javassist_" + counter++;
681     }
682 
make()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 
checkClassAndSuperName()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 
allocateClassName()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 
makeSortedMethodList()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 
computeSignature(MethodFilter filter)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 
installSignature(byte[] signature)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 
testBit(byte[] signature, int idx)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 
setBit(byte[] signature, int idx)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 
setInterfaces(ClassFile cf, Class[] interfaces)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 
addMethodsHolder(ClassFile cf, ConstPool cp, String classname, int size)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 
addSetter(String classname, ClassFile cf, ConstPool cp)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 
addGetter(String classname, ClassFile cf, ConstPool cp)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 
overrideMethods(ClassFile cf, ConstPool cp, String className)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 
override(String thisClassname, Method meth, String prefix, int index, String desc, ClassFile cf, ConstPool cp)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 
makeConstructors(String thisClassName, ClassFile cf, ConstPool cp, String classname)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 
makeUniqueName(String name, List sortedMethods)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 
makeUniqueName0(String name, Iterator it)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      */
isVisible(int mod, String from, Member meth)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 
getPackageName(String name)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 
getMethods(Class superClass, Class[] interfaceTypes)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 
getMethods(HashMap hash, Class clazz)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 
keyToDesc(String key)1050     private static String keyToDesc(String key) {
1051         return key.substring(key.indexOf(':') + 1);
1052     }
1053 
makeConstructor(String thisClassName, Constructor cons, ConstPool cp, Class superClass, boolean doHandlerInit)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 
makeDelegator(Method meth, String desc, ConstPool cp, Class declClass, String delegatorName)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      */
makeForwarder(String thisClassName, Method meth, String desc, ConstPool cp, Class declClass, String delegatorName, int index)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 
setThrows(MethodInfo minfo, ConstPool cp, Method orig)1173     private static void setThrows(MethodInfo minfo, ConstPool cp, Method orig) {
1174         Class[] exceptions = orig.getExceptionTypes();
1175         setThrows(minfo, cp, exceptions);
1176     }
1177 
setThrows(MethodInfo minfo, ConstPool cp, Class[] exceptions)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 
addLoadParameters(Bytecode code, Class[] params, int offset)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 
addLoad(Bytecode code, int n, Class type)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 
addReturn(Bytecode code, Class type)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 
makeParameterList(Bytecode code, Class[] params)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 
makeWrapper(Bytecode code, Class type, int regno)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      */
callFind2Methods(Bytecode code, String superMethod, String thisMethod, int index, String desc, int arrayVar)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 
addUnwrapper(Bytecode code, Class type)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 
makeWriteReplace(ConstPool cp)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