1 /*
2  * Javassist, a Java-bytecode translator toolkit.
3  * Copyright (C) 1999- 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  * or the Apache License Version 2.0.
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  */

16
17 package javassist.util.proxy;
18
19 import java.lang.ref.Reference;
20 import java.lang.ref.WeakReference;
21 import java.lang.reflect.Constructor;
22 import java.lang.reflect.Field;
23 import java.lang.reflect.InvocationTargetException;
24 import java.lang.reflect.Member;
25 import java.lang.reflect.Method;
26 import java.lang.reflect.Modifier;
27 import java.security.ProtectionDomain;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.Comparator;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Set;
37 import java.util.WeakHashMap;
38 import java.lang.invoke.MethodHandles.Lookup;
39
40 import javassist.CannotCompileException;
41 import javassist.bytecode.AccessFlag;
42 import javassist.bytecode.Bytecode;
43 import javassist.bytecode.ClassFile;
44 import javassist.bytecode.CodeAttribute;
45 import javassist.bytecode.ConstPool;
46 import javassist.bytecode.Descriptor;
47 import javassist.bytecode.DuplicateMemberException;
48 import javassist.bytecode.ExceptionsAttribute;
49 import javassist.bytecode.FieldInfo;
50 import javassist.bytecode.MethodInfo;
51 import javassist.bytecode.Opcode;
52 import javassist.bytecode.StackMapTable;
53
54 /*
55  * This class is implemented only with the lower-level API of Javassist.
56  * This design decision is for maximizing performance.
57  */

58
59 /**
60  * Factory of dynamic proxy classes.
61  *
62  * <p>This factory generates a class that extends the given super class and implements
63  * the given interfaces.  The calls of the methods inherited from the super class are
64  * forwarded and then <code>invoke()</code> is called on the method handler
65  * associated with instances of the generated class.  The calls of the methods from
66  * the interfaces are also forwarded to the method handler.
67  *
68  * <p>For example, if the following code is executed,
69  * 
70  * <pre>
71  * ProxyFactory f = new ProxyFactory();
72  * f.setSuperclass(Foo.class);
73  * f.setFilter(new MethodFilter() {
74  *     public boolean isHandled(Method m) {
75  *         // ignore finalize()
76  *         return !m.getName().equals("finalize");
77  *     }
78  * });
79  * Class c = f.createClass();
80  * MethodHandler mi = new MethodHandler() {
81  *     public Object invoke(Object self, Method m, Method proceed,
82  *                          Object[] args) throws Throwable {
83  *         System.out.println("Name: " + m.getName());
84  *         return proceed.invoke(self, args);  // execute the original method.
85  *     }
86  * };
87  * Foo foo = (Foo)c.newInstance();
88  * ((Proxy)foo).setHandler(mi);
89  * </pre>
90  *
91  * <p>Here, <code>Method</code> is <code>java.lang.reflect.Method</code>.</p>
92  *
93  * <p>Then, the following method call will be forwarded to MethodHandler
94  * <code>mi</code> and prints a message before executing the originally called method
95  * <code>bar()</code> in <code>Foo</code>.
96  *
97  * <pre>
98  * foo.bar();
99  * </pre>
100  *
101  * <p>The last three lines of the code shown above can be replaced with a call to
102  * the helper method <code>create</code>, which generates a proxy class, instantiates
103  * it, and sets the method handler of the instance:
104  *
105  * <pre>
106  *     :
107  * Foo foo = (Foo)f.create(new Class[0], new Object[0], mi);
108  * </pre>
109  *
110  * <p>To change the method handler during runtime,
111  * execute the following code:
112  *
113  * <pre>
114  * MethodHandler mi = ... ;    // alternative handler
115  * ((Proxy)foo).setHandler(mi);
116  * </pre>
117  *
118  * <p> If setHandler is never called for a proxy instance then it will
119  * employ the default handler which proceeds by invoking the original method.
120  * The behaviour of the default handler is identical to the following
121  * handler:
122  *
123  * <pre>
124  * class EmptyHandler implements MethodHandler {
125  *     public Object invoke(Object self, Method m,
126  *                          Method proceed, Object[] args) throws Exception {
127  *         return proceed.invoke(self, args);
128  *     }
129  * }
130  * </pre>
131  *
132  * <p>A proxy factory caches and reuses proxy classes by default. It is possible to reset
133  * this default globally by setting static field {@link ProxyFactory#useCache} to false.
134  * Caching may also be configured for a specific factory by calling instance method
135  * {@link ProxyFactory#setUseCache(boolean)}. It is strongly recommended that new clients
136  * of class ProxyFactory enable caching. Failure to do so may lead to exhaustion of
137  * the heap memory area used to store classes.
138  *
139  * <p>Caching is automatically disabled for any given proxy factory if deprecated instance
140  * method {@link ProxyFactory#setHandler(MethodHandler)} is called. This method was
141  * used to specify a default handler which newly created proxy classes should install
142  * when they create their instances. It is only retained to provide backward compatibility
143  * with previous releases of javassist. Unfortunately,this legacy behaviour makes caching
144  * and reuse of proxy classes impossible. The current programming model expects javassist
145  * clients to set the handler of a proxy instance explicitly by calling method
146  * {@link Proxy#setHandler(MethodHandler)} as shown in the sample code above. New
147  * clients are strongly recommended to use this model rather than calling
148  * {@link ProxyFactory#setHandler(MethodHandler)}.
149  *
150  * <p>A proxy object generated by <code>ProxyFactory</code> is serializable
151  * if its super class or any of its interfaces implement <code>java.io.Serializable</code>.
152  * However, a serialized proxy object may not be compatible with future releases.
153  * The serialization support should be used for short-term storage or RMI.
154  *
155  * <p>For compatibility with older releases serialization of proxy objects is implemented by
156  * adding a writeReplace method to the proxy class. This allows a proxy to be serialized
157  * to a conventional {@link java.io.ObjectOutputStream} and deserialized from a corresponding
158  * {@link java.io.ObjectInputStream}. However this method suffers from several problems, the most
159  * notable one being that it fails to serialize state inherited from the proxy's superclass.
160  * <p>
161  * An alternative method of serializing proxy objects is available which fixes these problems. It
162  * requires inhibiting generation of the writeReplace method and instead using instances of
163  * {@link javassist.util.proxy.ProxyObjectOutputStream} and {@link javassist.util.proxy.ProxyObjectInputStream}
164  * (which are subclasses of {@link java.io.ObjectOutputStream} and  {@link java.io.ObjectInputStream})
165  * to serialize and deserialize, respectively, the proxy. These streams recognise javassist proxies and ensure
166  * that they are serialized and deserialized without the need for the proxy class to implement special methods
167  * such as writeReplace. Generation of the writeReplace method can be disabled globally by setting static field
168  * {@link ProxyFactory#useWriteReplace} to false. Alternatively, it may be
169  * configured per factory by calling instance method {@link ProxyFactory#setUseWriteReplace(boolean)}.
170  *
171  * @see MethodHandler
172  * @since 3.1
173  * @author Muga Nishizawa
174  * @author Shigeru Chiba
175  * @author Andrew Dinn
176  */

177 public class ProxyFactory {
178     private Class<?> superClass;
179     private Class<?>[] interfaces;
180     private MethodFilter methodFilter;
181     private MethodHandler handler;  // retained for legacy usage
182     private List<Map.Entry<String,Method>> signatureMethods;
183     private boolean hasGetHandler;
184     private byte[] signature;
185     private String classname;
186     private String basename;
187     private String superName;
188     private Class<?> thisClass;
189     /**
190      * per factory setting initialised from current setting for useCache but able to be reset before each create call
191      */

192     private boolean factoryUseCache;
193     /**
194      * per factory setting initialised from current setting for useWriteReplace but able to be reset before each create call
195      */

196     private boolean factoryWriteReplace;
197
198     /**
199      * <p>If true, only public/protected methods are forwarded to a proxy object.
200      * The class for that proxy object is loaded by the {@code defineClass} method
201      * in {@code java.lang.invoke.MethodHandles.Lookup}, which is available in
202      * Java 9 or later.  This works even when {@code sun.misc.Unsafe} is not
203      * available for some reasons (it is already deprecated in Java 9).</p>
204      *
205      * <p>To load a class, Javassist first tries to use {@code sun.misc.Unsafe} and,
206      * if not available, it uses a {@code protected} method in {@code java.lang.ClassLoader}
207      * via {@code PrivilegedAction}.  Since the latter approach is not available
208      * any longer by default in Java 9 or later, the JVM argument
209      * {@code --add-opens java.base/java.lang=ALL-UNNAMED} must be given to the JVM
210      * when it is used (because of lack of {@code sun.misc.Unsafe}).
211      * If this argument cannot be given to the JVM, {@code onlyPublicMethods} should
212      * be set to {@code true}.  Javassist will try to load by using
213      * {@code java.lang.invoke.MethodHandles.Lookup}.</p>
214      *
215      * <p>The default value is {@code false}.</p>
216      *
217      * @see DefineClassHelper#toClass(String, Class, ClassLoader, ProtectionDomain, byte[])
218      * @since 3.22
219      */

220     public static boolean onlyPublicMethods = false;
221
222     /**
223      * If the value of this variable is not null, the class file of
224      * the generated proxy class is written under the directory specified
225      * by this variable.  For example, if the value is 
226      * <code>"."</code>, then the class file is written under the current
227      * directory.  This method is for debugging.
228      *
229      * <p>The default value is null.
230      */

231     public String writeDirectory;
232
233     private static final Class<?> OBJECT_TYPE = Object.class;
234
235     private static final String HOLDER = "_methods_";
236     private static final String HOLDER_TYPE = "[Ljava/lang/reflect/Method;";
237     private static final String FILTER_SIGNATURE_FIELD = "_filter_signature";
238     private static final String FILTER_SIGNATURE_TYPE = "[B";
239     private static final String HANDLER = "handler";
240     private static final String NULL_INTERCEPTOR_HOLDER = "javassist.util.proxy.RuntimeSupport";
241     private static final String DEFAULT_INTERCEPTOR = "default_interceptor";
242     private static final String HANDLER_TYPE
243         = 'L' + MethodHandler.class.getName().replace('.', '/') + ';';
244     private static final String HANDLER_SETTER = "setHandler";
245     private static final String HANDLER_SETTER_TYPE = "(" + HANDLER_TYPE + ")V";
246
247     private static final String HANDLER_GETTER = "getHandler";
248     private static final String HANDLER_GETTER_TYPE = "()" + HANDLER_TYPE;
249
250     private static final String SERIAL_VERSION_UID_FIELD = "serialVersionUID";
251     private static final String SERIAL_VERSION_UID_TYPE = "J";
252     private static final long SERIAL_VERSION_UID_VALUE = -1L;
253
254     /**
255      * If true, a generated proxy class is cached and it will be reused
256      * when generating the proxy class with the same properties is requested.
257      * The default value is true.
258      *
259      * Note that this value merely specifies the initial setting employed by any newly created
260      * proxy factory. The factory setting may be overwritten by calling factory instance method
261      * {@link #setUseCache(boolean)}
262      *
263      * @since 3.4
264      */

265     public static volatile boolean useCache = true;
266
267     /**
268      * If true, a generated proxy class will implement method writeReplace enabling
269      * serialization of its proxies to a conventional ObjectOutputStream. this (default)
270      * setting retains the old javassist behaviour which has the advantage that it
271      * retains compatibility with older  releases and requires no extra work on the part
272      * of the client performing the serialization. However, it has the disadvantage that
273      * state inherited from the superclasses of the proxy is lost during serialization.
274      * if false then serialization/deserialization of the proxy instances will preserve
275      * all fields. However, serialization must be performed via a {@link ProxyObjectOutputStream}
276      * and deserialization must be via {@link ProxyObjectInputStream}. Any attempt to serialize
277      * proxies whose class was created with useWriteReplace set to false via a normal
278      * {@link java.io.ObjectOutputStream} will fail.
279      *
280      * Note that this value merely specifies the initial setting employed by any newly created
281      * proxy factory. The factory setting may be overwritten by calling factory instance method
282      * {@link #setUseWriteReplace(boolean)}
283      *
284      * @since 3.4
285      */

286     public static volatile boolean useWriteReplace = true;
287
288     /*
289      * methods allowing individual factory settings for factoryUseCache and factoryWriteReplace to be reset
290      */

291
292     /**
293      * test whether this factory uses the proxy cache
294      * @return true if this factory uses the proxy cache otherwise false
295      */

296     public boolean isUseCache()
297     {
298         return factoryUseCache;
299     }
300
301     /**
302      * configure whether this factory should use the proxy cache
303      * @param useCache true if this factory should use the proxy cache and false if it should not use the cache
304      * @throws RuntimeException if a default interceptor has been set for the factory
305      */

306     public void setUseCache(boolean useCache)
307     {
308         // we cannot allow caching to be used if the factory is configured to install a default interceptor
309         // field into generated classes
310         if (handler != null && useCache) {
311             throw new RuntimeException("caching cannot be enabled if the factory default interceptor has been set");
312         }
313         factoryUseCache = useCache;
314     }
315
316     /**
317      * test whether this factory installs a writeReplace method in created classes
318      * @return true if this factory installs a writeReplace method in created classes otherwise false
319      */

320     public boolean isUseWriteReplace()
321     {
322         return factoryWriteReplace;
323     }
324
325     /**
326      * configure whether this factory should add a writeReplace method to created classes
327      * @param useWriteReplace true if this factory should add a writeReplace method to created classes and false if it
328      * should not add a writeReplace method
329      */

330     public void setUseWriteReplace(boolean useWriteReplace)
331     {
332         factoryWriteReplace = useWriteReplace;
333     }
334
335     private static Map<ClassLoader,Map<String,ProxyDetails>> proxyCache =
336             new WeakHashMap<ClassLoader,Map<String,ProxyDetails>>();
337
338     /**
339      * determine if a class is a javassist proxy class
340      * @param cl
341      * @return true if the class is a javassist proxy class otherwise false
342      */

343     public static boolean isProxyClass(Class<?> cl)
344     {
345         // all proxies implement Proxy or ProxyObject. nothing else should. 
346         return (Proxy.class.isAssignableFrom(cl));
347     }
348
349     /**
350      * used to store details of a specific proxy class in the second tier of the proxy cache. this entry
351      * will be located in a hashmap keyed by the unique identifying name of the proxy class. the hashmap is
352      * located in a weak hashmap keyed by the classloader common to all proxy classes in the second tier map.
353      */

354     static class ProxyDetails {
355         /**
356          * the unique signature of any method filter whose behaviour will be met by this class. each bit in
357          * the byte array is set if the filter redirects the corresponding super or interface method and clear
358          * if it does not redirect it.
359          */

360         byte[] signature;
361         /**
362          * a hexadecimal string representation of the signature bit sequence. this string also forms part
363          * of the proxy class name.
364          */

365         Reference<Class<?>> proxyClass;
366         /**
367          * a flag which is true this class employs writeReplace to perform serialization of its instances
368          * and false if serialization must employ of a ProxyObjectOutputStream and ProxyObjectInputStream
369          */

370         boolean isUseWriteReplace;
371
372         ProxyDetails(byte[] signature, Class<?> proxyClass, boolean isUseWriteReplace)
373         {
374             this.signature = signature;
375             this.proxyClass = new WeakReference<Class<?>>(proxyClass);
376             this.isUseWriteReplace = isUseWriteReplace;
377         }
378     }
379
380     /**
381      * Constructs a factory of proxy class.
382      */

383     public ProxyFactory() {
384         superClass = null;
385         interfaces = null;
386         methodFilter = null;
387         handler = null;
388         signature = null;
389         signatureMethods = null;
390         hasGetHandler = false;
391         thisClass = null;
392         writeDirectory = null;
393         factoryUseCache = useCache;
394         factoryWriteReplace = useWriteReplace;
395     }
396
397     /**
398      * Sets the super class of a proxy class.
399      */

400     public void setSuperclass(Class<?> clazz) {
401         superClass = clazz;
402         // force recompute of signature
403         signature = null;
404     }
405
406     /**
407      * Obtains the super class set by <code>setSuperclass()</code>.
408      *
409      * @since 3.4
410      */

411     public Class<?> getSuperclass() { return superClass; }
412
413     /**
414      * Sets the interfaces of a proxy class.
415      */

416     public void setInterfaces(Class<?>[] ifs) {
417         interfaces = ifs;
418         // force recompute of signature
419         signature = null;
420     }
421
422     /**
423      * Obtains the interfaces set by <code>setInterfaces</code>.
424      *
425      * @since 3.4
426      */

427     public Class<?>[] getInterfaces() { return interfaces; }
428
429     /**
430      * Sets a filter that selects the methods that will be controlled by a handler.
431      */

432     public void setFilter(MethodFilter mf) {
433         methodFilter = mf;
434         // force recompute of signature
435         signature = null;
436     }
437
438     /**
439      * Generates a proxy class using the current filter.
440      * The module or package where a proxy class is created
441      * has to be opened to this package or the Javassist module.
442      *
443      * @see #createClass(Lookup)
444      */

445     public Class<?> createClass() {
446         if (signature == null) {
447             computeSignature(methodFilter);
448         }
449         return createClass1(null);
450     }
451
452     /**
453      * Generates a proxy class using the supplied filter.
454      * The module or package where a proxy class is created
455      * has to be opened to this package or the Javassist module.
456      */

457     public Class<?> createClass(MethodFilter filter) {
458         computeSignature(filter);
459         return createClass1(null);
460     }
461
462     /**
463      * Generates a proxy class with a specific signature.
464      * access is package local so ProxyObjectInputStream can use this
465      * @param signature
466      */

467     Class<?> createClass(byte[] signature)
468     {
469         installSignature(signature);
470         return createClass1(null);
471     }
472
473     /**
474      * Generates a proxy class using the current filter.
475      *
476      * @param lookup    used for loading the proxy class.
477      *                  It needs an appropriate right to invoke {@code defineClass}
478      *                  for the proxy class.
479      * @since 3.24
480      */

481     public Class<?> createClass(Lookup lookup) {
482         if (signature == null) {
483             computeSignature(methodFilter);
484         }
485         return createClass1(lookup);
486     }
487
488     /**
489      * Generates a proxy class using the supplied filter.
490      *
491      * @param lookup    used for loading the proxy class.
492      *                  It needs an appropriate right to invoke {@code defineClass}
493      *                  for the proxy class.
494      * @param filter    the filter.
495      * @since 3.24
496      */

497     public Class<?> createClass(Lookup lookup, MethodFilter filter) {
498         computeSignature(filter);
499         return createClass1(lookup);
500     }
501
502     /**
503      * Generates a proxy class with a specific signature.
504      * access is package local so ProxyObjectInputStream can use this.
505      *
506      * @param lookup    used for loading the proxy class.
507      *                  It needs an appropriate right to invoke {@code defineClass}
508      *                  for the proxy class.
509      * @param signature         the signature.
510      */

511     Class<?> createClass(Lookup lookup, byte[] signature)
512     {
513         installSignature(signature);
514         return createClass1(lookup);
515     }
516
517     private Class<?> createClass1(Lookup lookup) {
518         Class<?> result = thisClass;
519         if (result == null) {
520             ClassLoader cl = getClassLoader();
521             synchronized (proxyCache) {
522                 if (factoryUseCache)
523                     createClass2(cl, lookup);
524                 else
525                     createClass3(cl, lookup);
526
527                 result = thisClass;
528                 // don't retain any unwanted references
529                 thisClass = null;
530             }
531         }
532
533         return result;
534     }
535
536     private static char[] hexDigits =
537             { '0', '1', '2', '3', '4', '5', '6', '7',
538             '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
539
540     public String getKey(Class<?> superClass, Class<?>[] interfaces, byte[] signature, boolean useWriteReplace)
541     {
542         StringBuffer sbuf = new StringBuffer();
543         if (superClass != null){
544             sbuf.append(superClass.getName());
545         }
546         sbuf.append(":");
547         for (int i = 0; i < interfaces.length; i++) {
548             sbuf.append(interfaces[i].getName());
549             sbuf.append(":");
550         }
551         for (int i = 0; i < signature.length; i++) {
552             byte b = signature[i];
553             int lo = b & 0xf;
554             int hi = (b >> 4) & 0xf;
555             sbuf.append(hexDigits[lo]);
556             sbuf.append(hexDigits[hi]);
557         }
558         if (useWriteReplace) {
559             sbuf.append(":w");
560         }
561
562         return sbuf.toString();
563     }
564
565     private void createClass2(ClassLoader cl, Lookup lookup) {
566         String key = getKey(superClass, interfaces, signature, factoryWriteReplace);
567         /*
568          * Excessive concurrency causes a large memory footprint and slows the
569          * execution speed down (with JDK 1.5).  Thus, we use a jumbo lock for
570          * reducing concrrency.
571          */

572         // synchronized (proxyCache) {
573             Map<String,ProxyDetails> cacheForTheLoader = proxyCache.get(cl);
574             ProxyDetails details;
575             if (cacheForTheLoader == null) {
576                 cacheForTheLoader = new HashMap<String,ProxyDetails>();
577                 proxyCache.put(cl, cacheForTheLoader);
578             }
579             details = cacheForTheLoader.get(key);
580             if (details != null) {
581                 Reference<Class<?>> reference = details.proxyClass;
582                 thisClass = reference.get();
583                 if (thisClass != null) {
584                     return;
585                 }
586             }
587             createClass3(cl, lookup);
588             details = new  ProxyDetails(signature, thisClass, factoryWriteReplace);
589             cacheForTheLoader.put(key, details);
590         // }
591     }
592
593     private void createClass3(ClassLoader cl, Lookup lookup) {
594         // we need a new class so we need a new class name
595         allocateClassName();
596
597         try {
598             ClassFile cf = make();
599             if (writeDirectory != null)
600                 FactoryHelper.writeFile(cf, writeDirectory);
601
602             if (lookup == null)
603                 thisClass = FactoryHelper.toClass(cf, getClassInTheSamePackage(), cl, getDomain());
604             else
605                 thisClass = FactoryHelper.toClass(cf, lookup);
606
607             setField(FILTER_SIGNATURE_FIELD, signature);
608             // legacy behaviour : we only set the default interceptor static field if we are not using the cache
609             if (!factoryUseCache) {
610                 setField(DEFAULT_INTERCEPTOR, handler);
611             }
612         }
613         catch (CannotCompileException e) {
614             throw new RuntimeException(e.getMessage(), e);
615         }
616
617     }
618
619     /**
620      * Obtains a class belonging to the same package that the created
621      * proxy class belongs to.  It is used to obtain an appropriate
622      * {@code java.lang.invoke.MethodHandles.Lookup}.
623      */

624     private Class<?> getClassInTheSamePackage() {
625         if (superClass != null && superClass != OBJECT_TYPE)
626             return superClass;
627         else if (interfaces != null && interfaces.length > 0)
628             return interfaces[0];
629         else
630             return this.getClass();     // maybe wrong?
631     }
632
633     private void setField(String fieldName, Object value) {
634         if (thisClass != null && value != null)
635             try {
636                 Field f = thisClass.getField(fieldName);
637                 SecurityActions.setAccessible(f, true);
638                 f.set(null, value);
639                 SecurityActions.setAccessible(f, false);
640             }
641             catch (Exception e) {
642                 throw new RuntimeException(e);
643             }
644     }
645
646     static byte[] getFilterSignature(Class<?> clazz) {
647         return (byte[])getField(clazz, FILTER_SIGNATURE_FIELD);
648     }
649
650     private static Object getField(Class<?> clazz, String fieldName) {
651         try {
652             Field f = clazz.getField(fieldName);
653             f.setAccessible(true);
654             Object value = f.get(null);
655             f.setAccessible(false);
656             return value;
657         }
658         catch (Exception e) {
659             throw new RuntimeException(e);
660         }
661     }
662
663     /**
664      * Obtains the method handler of the given proxy object.
665      * 
666      * @param p     a proxy object.
667      * @return the method handler.
668      * @since 3.16
669      */

670     public static MethodHandler getHandler(Proxy p) {
671         try {
672             Field f = p.getClass().getDeclaredField(HANDLER);
673             f.setAccessible(true);
674             Object value = f.get(p);
675             f.setAccessible(false);
676             return (MethodHandler)value;
677         }
678         catch (Exception e) {
679             throw new RuntimeException(e);
680         }
681     }
682
683     /**
684      * A provider of class loaders.
685      *
686      * @see #classLoaderProvider
687      * @since 3.4
688      */

689     public static interface ClassLoaderProvider {
690         /**
691          * Returns a class loader.
692          *
693          * @param pf    a proxy factory that is going to obtain a class loader.
694          */

695         public ClassLoader get(ProxyFactory pf);
696     }
697
698     /**
699      * A provider used by <code>createClass()</code> for obtaining
700      * a class loader.
701      * <code>get()</code> on this <code>ClassLoaderProvider</code> object
702      * is called to obtain a class loader.
703      *
704      * <p>The value of this field can be updated for changing the default
705      * implementation.
706      *
707      * <p>Example:
708      * <pre>
709      * ProxyFactory.classLoaderProvider = new ProxyFactory.ClassLoaderProvider() {
710      *     public ClassLoader get(ProxyFactory pf) {
711      *         return Thread.currentThread().getContextClassLoader();
712      *     }
713      * };
714      * </pre>
715      *
716      * @since 3.4
717      */

718     public static ClassLoaderProvider classLoaderProvider =
719         new ClassLoaderProvider() {
720             @Override
721             public ClassLoader get(ProxyFactory pf) {
722                   return pf.getClassLoader0();
723               }
724         };
725
726     protected ClassLoader getClassLoader() {
727         return classLoaderProvider.get(this);
728     }
729
730     protected ClassLoader getClassLoader0() {
731         ClassLoader loader = null;
732         if (superClass != null && !superClass.getName().equals("java.lang.Object"))
733             loader = superClass.getClassLoader();
734         else if (interfaces != null && interfaces.length > 0)
735             loader = interfaces[0].getClassLoader();
736  
737         if (loader == null) {
738             loader = getClass().getClassLoader();
739             // In case javassist is in the endorsed dir
740             if (loader == null) {
741                 loader = Thread.currentThread().getContextClassLoader();
742                 if (loader == null)
743                     loader = ClassLoader.getSystemClassLoader();
744             }
745         }
746
747         return loader;
748     }
749
750     protected ProtectionDomain getDomain() {
751         Class<?> clazz;
752         if (superClass != null && !superClass.getName().equals("java.lang.Object"))
753             clazz = superClass;
754         else if (interfaces != null && interfaces.length > 0)
755             clazz = interfaces[0];
756         else
757             clazz = this.getClass();
758
759         return clazz.getProtectionDomain();
760     }
761
762     /**
763      * Creates a proxy class and returns an instance of that class.
764      *
765      * @param paramTypes    parameter types for a constructor.
766      * @param args          arguments passed to a constructor.
767      * @param mh            the method handler for the proxy class.
768      * @since 3.4
769      */

770     public Object create(Class<?>[] paramTypes, Object[] args, MethodHandler mh)
771         throws NoSuchMethodException, IllegalArgumentException,
772                InstantiationException, IllegalAccessException, InvocationTargetException
773     {
774         Object obj = create(paramTypes, args);
775         ((Proxy)obj).setHandler(mh);
776         return obj;
777     }
778
779     /**
780      * Creates a proxy class and returns an instance of that class.
781      *
782      * @param paramTypes    parameter types for a constructor.
783      * @param args          arguments passed to a constructor.
784      */

785     public Object create(Class<?>[] paramTypes, Object[] args)
786         throws NoSuchMethodException, IllegalArgumentException,
787                InstantiationException, IllegalAccessException, InvocationTargetException
788     {
789         Class<?> c = createClass();
790         Constructor<?> cons = c.getConstructor(paramTypes);
791         return cons.newInstance(args);
792     }
793
794     /**
795      * Sets the default invocation handler.  This invocation handler is shared
796      * among all the instances of a proxy class unless another is explicitly
797      * specified.
798      * @deprecated since 3.12
799      * use of this method is incompatible  with proxy class caching.
800      * instead clients should call method {@link Proxy#setHandler(MethodHandler)} to set the handler
801      * for each newly created  proxy instance.
802      * calling this method will automatically disable caching of classes created by the proxy factory.
803      */

804     @Deprecated
805     public void setHandler(MethodHandler mi) {
806         // if we were using the cache and the handler is non-null then we must stop caching
807         if (factoryUseCache && mi != null)  {
808             factoryUseCache = false;
809             // clear any currently held class so we don't try to reuse it or set its handler field
810           thisClass  = null;
811         }
812         handler = mi;
813         // this retains the behaviour of the old code which resets any class we were holding on to
814         // this is probably not what is wanted
815         setField(DEFAULT_INTERCEPTOR, handler);
816     }
817
818     /**
819      * A unique class name generator.
820      */

821     public static interface UniqueName {
822         /**
823          * Returns a unique class name.
824          *
825          * @param classname     the super class name of the proxy class.
826          */

827         String get(String classname);
828     }
829
830     /**
831      * A unique class name generator.
832      * Replacing this generator changes the algorithm to generate a
833      * unique name. The <code>get</code> method does not have to be
834      * a <code>synchronized</code> method since the access to this field
835      * is mutually exclusive and thus thread safe.
836      */

837     public static UniqueName nameGenerator = new UniqueName() {
838         private final String sep = "_$$_jvst" + Integer.toHexString(this.hashCode() & 0xfff) + "_";
839         private int counter = 0;
840
841         @Override
842         public String get(String classname) {
843             return classname + sep + Integer.toHexString(counter++);
844         }
845     };
846
847     private static String makeProxyName(String classname) {
848         synchronized (nameGenerator) {
849             return nameGenerator.get(classname);
850         }
851     }
852
853     private ClassFile make() throws CannotCompileException {
854         ClassFile cf = new ClassFile(false, classname, superName);
855         cf.setAccessFlags(AccessFlag.PUBLIC);
856         setInterfaces(cf, interfaces, hasGetHandler ? Proxy.class : ProxyObject.class);
857         ConstPool pool = cf.getConstPool();
858
859         // legacy: we only add the static field for the default interceptor if caching is disabled
860         if  (!factoryUseCache) {
861             FieldInfo finfo = new FieldInfo(pool, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
862             finfo.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
863             cf.addField(finfo);
864         }
865
866         // handler is per instance
867         FieldInfo finfo2 = new FieldInfo(pool, HANDLER, HANDLER_TYPE);
868         finfo2.setAccessFlags(AccessFlag.PRIVATE);
869         cf.addField(finfo2);
870
871         // filter signature is per class
872         FieldInfo finfo3 = new FieldInfo(pool, FILTER_SIGNATURE_FIELD, FILTER_SIGNATURE_TYPE);
873         finfo3.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
874         cf.addField(finfo3);
875
876         // the proxy class serial uid must always be a fixed value
877         FieldInfo finfo4 = new FieldInfo(pool, SERIAL_VERSION_UID_FIELD, SERIAL_VERSION_UID_TYPE);
878         finfo4.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC| AccessFlag.FINAL);
879         cf.addField(finfo4);
880
881         // HashMap allMethods = getMethods(superClass, interfaces);
882         // int size = allMethods.size();
883         makeConstructors(classname, cf, pool, classname);
884
885         List<Find2MethodsArgs> forwarders = new ArrayList<Find2MethodsArgs>();
886         int s = overrideMethods(cf, pool, classname, forwarders);
887         addClassInitializer(cf, pool, classname, s, forwarders);
888         addSetter(classname, cf, pool);
889         if (!hasGetHandler)
890             addGetter(classname, cf, pool);
891
892         if (factoryWriteReplace) {
893             try {
894                 cf.addMethod(makeWriteReplace(pool));
895             }
896             catch (DuplicateMemberException e) {
897                 // writeReplace() is already declared in the super class/interfaces.
898             }
899         }
900
901         thisClass = null;
902         return cf;
903     }
904
905     private void checkClassAndSuperName() {
906         if (interfaces == null)
907             interfaces = new Class[0];
908
909         if (superClass == null) {
910             superClass = OBJECT_TYPE;
911             superName = superClass.getName();
912             basename = interfaces.length == 0 ? superName
913                                               : interfaces[0].getName();
914         } else {
915             superName = superClass.getName();
916             basename = superName;
917         }
918
919         if (Modifier.isFinal(superClass.getModifiers()))
920             throw new RuntimeException(superName + " is final");
921
922         if (basename.startsWith("java.") || onlyPublicMethods)
923             basename = "javassist.util.proxy." + basename.replace('.', '_');
924     }
925
926     private void allocateClassName() {
927         classname = makeProxyName(basename);
928     }
929
930     private static Comparator<Map.Entry<String,Method>> sorter =
931         new Comparator<Map.Entry<String,Method>>() {
932             @Override
933             public int compare(Map.Entry<String,Method> e1, Map.Entry<String,Method> e2) {
934                 return e1.getKey().compareTo(e2.getKey());
935             }
936         };
937
938     private void makeSortedMethodList() {
939         checkClassAndSuperName();
940
941         hasGetHandler = false;      // getMethods() may set this to true.
942         Map<String,Method> allMethods = getMethods(superClass, interfaces);
943         signatureMethods = new ArrayList<Map.Entry<String,Method>>(allMethods.entrySet());
944         Collections.sort(signatureMethods, sorter);
945     }
946
947     private void computeSignature(MethodFilter filter) // throws CannotCompileException
948     {
949         makeSortedMethodList();
950
951         int l = signatureMethods.size();
952         int maxBytes = ((l + 7) >> 3);
953         signature = new byte[maxBytes];
954         for (int idx = 0; idx < l; idx++)
955         {
956             Method m = signatureMethods.get(idx).getValue();
957             int mod = m.getModifiers();
958             if (!Modifier.isFinal(mod) && !Modifier.isStatic(mod)
959                     && isVisible(mod, basename, m) && (filter == null || filter.isHandled(m))) {
960                 setBit(signature, idx);
961             }
962         }
963     }
964
965     private void installSignature(byte[] signature) // throws CannotCompileException
966     {
967         makeSortedMethodList();
968
969         int l = signatureMethods.size();
970         int maxBytes = ((l + 7) >> 3);
971         if (signature.length != maxBytes) {
972             throw new RuntimeException("invalid filter signature length for deserialized proxy class");
973         }
974
975         this.signature =  signature;
976     }
977
978     private boolean testBit(byte[] signature, int idx) {
979         int byteIdx = idx >> 3;
980         if (byteIdx > signature.length)
981             return false;
982         int bitIdx = idx & 0x7;
983         int mask = 0x1 << bitIdx;
984         int sigByte = signature[byteIdx];
985         return ((sigByte & mask) != 0);
986     }
987
988     private void setBit(byte[] signature, int idx) {
989         int byteIdx = idx >> 3;
990         if (byteIdx < signature.length) {
991             int bitIdx = idx & 0x7;
992             int mask = 0x1 << bitIdx;
993             int sigByte = signature[byteIdx];
994             signature[byteIdx] = (byte)(sigByte | mask);
995         }
996     }
997
998     private static void setInterfaces(ClassFile cf, Class<?>[] interfaces, Class<?> proxyClass) {
999         String setterIntf = proxyClass.getName();
1000         String[] list;
1001         if (interfaces == null || interfaces.length == 0)
1002             list = new String[] { setterIntf };
1003         else {
1004             list = new String[interfaces.length + 1];
1005             for (int i = 0; i < interfaces.length; i++)
1006                 list[i] = interfaces[i].getName();
1007
1008             list[interfaces.length] = setterIntf;
1009         }
1010
1011         cf.setInterfaces(list);
1012     }
1013
1014     private static void addClassInitializer(ClassFile cf, ConstPool cp,
1015                 String classname, int size, List<Find2MethodsArgs> forwarders)
1016         throws CannotCompileException
1017     {
1018         FieldInfo finfo = new FieldInfo(cp, HOLDER, HOLDER_TYPE);
1019         finfo.setAccessFlags(AccessFlag.PRIVATE | AccessFlag.STATIC);
1020         cf.addField(finfo);
1021         MethodInfo minfo = new MethodInfo(cp, "<clinit>""()V");
1022         minfo.setAccessFlags(AccessFlag.STATIC);
1023         setThrows(minfo, cp, new Class<?>[] { ClassNotFoundException.class });
1024
1025         Bytecode code = new Bytecode(cp, 0, 2);
1026         code.addIconst(size * 2);
1027         code.addAnewarray("java.lang.reflect.Method");
1028         final int varArray = 0;
1029         code.addAstore(varArray);
1030
1031         // forName() must be called here.  Otherwise, the class might be
1032         // invisible.
1033         code.addLdc(classname);
1034         code.addInvokestatic("java.lang.Class",
1035                 "forName""(Ljava/lang/String;)Ljava/lang/Class;");
1036         final int varClass = 1;
1037         code.addAstore(varClass);
1038
1039         for (Find2MethodsArgs args:forwarders)
1040             callFind2Methods(code, args.methodName, args.delegatorName,
1041                              args.origIndex, args.descriptor, varClass, varArray);
1042
1043         code.addAload(varArray);
1044         code.addPutstatic(classname, HOLDER, HOLDER_TYPE);
1045
1046         code.addLconst(SERIAL_VERSION_UID_VALUE);
1047         code.addPutstatic(classname, SERIAL_VERSION_UID_FIELD, SERIAL_VERSION_UID_TYPE);
1048         code.addOpcode(Bytecode.RETURN);
1049         minfo.setCodeAttribute(code.toCodeAttribute());
1050         cf.addMethod(minfo);
1051     }
1052
1053     /**
1054      * @param thisMethod        might be null.
1055      */

1056     private static void callFind2Methods(Bytecode code, String superMethod, String thisMethod,
1057                                          int index, String desc, int classVar, int arrayVar) {
1058         String findClass = RuntimeSupport.class.getName();
1059         String findDesc
1060             = "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;[Ljava/lang/reflect/Method;)V";
1061
1062         code.addAload(classVar);
1063         code.addLdc(superMethod);
1064         if (thisMethod == null)
1065             code.addOpcode(Opcode.ACONST_NULL);
1066         else
1067             code.addLdc(thisMethod);
1068
1069         code.addIconst(index);
1070         code.addLdc(desc);
1071         code.addAload(arrayVar);
1072         code.addInvokestatic(findClass, "find2Methods", findDesc);
1073     }
1074
1075     private static void addSetter(String classname, ClassFile cf, ConstPool cp)
1076         throws CannotCompileException
1077     {
1078         MethodInfo minfo = new MethodInfo(cp, HANDLER_SETTER,
1079                                           HANDLER_SETTER_TYPE);
1080         minfo.setAccessFlags(AccessFlag.PUBLIC);
1081         Bytecode code = new Bytecode(cp, 2, 2);
1082         code.addAload(0);
1083         code.addAload(1);
1084         code.addPutfield(classname, HANDLER, HANDLER_TYPE);
1085         code.addOpcode(Bytecode.RETURN);
1086         minfo.setCodeAttribute(code.toCodeAttribute());
1087         cf.addMethod(minfo);
1088     }
1089
1090     private static void addGetter(String classname, ClassFile cf, ConstPool cp)
1091         throws CannotCompileException
1092     {
1093         MethodInfo minfo = new MethodInfo(cp, HANDLER_GETTER,
1094                                           HANDLER_GETTER_TYPE);
1095         minfo.setAccessFlags(AccessFlag.PUBLIC);
1096         Bytecode code = new Bytecode(cp, 1, 1);
1097         code.addAload(0);
1098         code.addGetfield(classname, HANDLER, HANDLER_TYPE);
1099         code.addOpcode(Bytecode.ARETURN);
1100         minfo.setCodeAttribute(code.toCodeAttribute());
1101         cf.addMethod(minfo);
1102     }
1103
1104     private int overrideMethods(ClassFile cf, ConstPool cp, String className, List<Find2MethodsArgs> forwarders)
1105         throws CannotCompileException
1106     {
1107         String prefix = makeUniqueName("_d", signatureMethods);
1108         Iterator<Map.Entry<String,Method>> it = signatureMethods.iterator();
1109         int index = 0;
1110         while (it.hasNext()) {
1111             Map.Entry<String,Method> e = it.next();
1112             if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_5 || !isBridge(e.getValue()))
1113                 if (testBit(signature, index)) {
1114                     override(className, e.getValue(), prefix, index,
1115                              keyToDesc(e.getKey(), e.getValue()), cf, cp, forwarders);
1116                 }
1117
1118             index++;
1119         }
1120
1121         return index;
1122     }
1123
1124     private static boolean isBridge(Method m) {
1125         return m.isBridge();
1126     }
1127
1128     private void override(String thisClassname, Method meth, String prefix,
1129                           int index, String desc, ClassFile cf, ConstPool cp,
1130                           List<Find2MethodsArgs> forwarders)
1131         throws CannotCompileException
1132     {
1133         Class<?> declClass = meth.getDeclaringClass();
1134         String delegatorName = prefix + index + meth.getName();
1135         if (Modifier.isAbstract(meth.getModifiers()))
1136             delegatorName = null;
1137         else {
1138             MethodInfo delegator
1139                 = makeDelegator(meth, desc, cp, declClass, delegatorName);
1140             // delegator is not a bridge method.  See Sec. 15.12.4.5 of JLS 3rd Ed.
1141             delegator.setAccessFlags(delegator.getAccessFlags() & ~AccessFlag.BRIDGE);
1142             cf.addMethod(delegator);
1143         }
1144
1145         MethodInfo forwarder
1146             = makeForwarder(thisClassname, meth, desc, cp, declClass,
1147                             delegatorName, index, forwarders);
1148         cf.addMethod(forwarder);
1149     }
1150
1151     private void makeConstructors(String thisClassName, ClassFile cf,
1152             ConstPool cp, String classname) throws CannotCompileException
1153     {
1154         Constructor<?>[] cons = SecurityActions.getDeclaredConstructors(superClass);
1155         // legacy: if we are not caching then we need to initialise the default handler
1156         boolean doHandlerInit = !factoryUseCache;
1157         for (int i = 0; i < cons.length; i++) {
1158             Constructor<?> c = cons[i];
1159             int mod = c.getModifiers();
1160             if (!Modifier.isFinal(mod) && !Modifier.isPrivate(mod)
1161                     && isVisible(mod, basename, c)) {
1162                 MethodInfo m = makeConstructor(thisClassName, c, cp, superClass, doHandlerInit);
1163                 cf.addMethod(m);
1164             }
1165         }
1166     }
1167
1168     private static String makeUniqueName(String name, List<Map.Entry<String,Method>> sortedMethods) {
1169         if (makeUniqueName0(name, sortedMethods.iterator()))
1170             return name;
1171
1172         for (int i = 100; i < 999; i++) {
1173             String s = name + i;
1174             if (makeUniqueName0(s, sortedMethods.iterator()))
1175                 return s;
1176         }
1177
1178         throw new RuntimeException("cannot make a unique method name");
1179     }
1180
1181     private static boolean makeUniqueName0(String name, Iterator<Map.Entry<String,Method>> it) {
1182         while (it.hasNext())
1183             if (it.next().getKey().startsWith(name))
1184                 return false;
1185         return true;
1186     }
1187
1188     /**
1189      * Returns true if the method is visible from the package.
1190      *
1191      * @param mod       the modifiers of the method. 
1192      */

1193     private static boolean isVisible(int mod, String from, Member meth) {
1194         if ((mod & Modifier.PRIVATE) != 0)
1195             return false;
1196         else if ((mod & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0)
1197             return true;
1198         else {
1199             String p = getPackageName(from);
1200             String q = getPackageName(meth.getDeclaringClass().getName());
1201             if (p == null)
1202                 return q == null;
1203             return p.equals(q);
1204         }
1205     }
1206
1207     private static String getPackageName(String name) {
1208         int i = name.lastIndexOf('.');
1209         if (i < 0)
1210             return null;
1211         return name.substring(0, i);
1212     }
1213
1214     /* getMethods() may set hasGetHandler to true.
1215      */

1216     private Map<String,Method> getMethods(Class<?> superClass, Class<?>[] interfaceTypes) {
1217         Map<String,Method> hash = new HashMap<String,Method>();
1218         Set<Class<?>> set = new HashSet<Class<?>>();
1219         for (int i = 0; i < interfaceTypes.length; i++)
1220             getMethods(hash, interfaceTypes[i], set);
1221
1222         getMethods(hash, superClass, set);
1223         return hash;
1224     }
1225
1226     private void getMethods(Map<String,Method> hash, Class<?> clazz, Set<Class<?>> visitedClasses) {
1227         // This both speeds up scanning by avoiding duplicate interfaces and is needed to
1228         // ensure that superinterfaces are always scanned before subinterfaces.
1229         if (!visitedClasses.add(clazz))
1230             return;
1231
1232         Class<?>[] ifs = clazz.getInterfaces();
1233         for (int i = 0; i < ifs.length; i++)
1234             getMethods(hash, ifs[i], visitedClasses);
1235
1236         Class<?> parent = clazz.getSuperclass();
1237         if (parent != null)
1238             getMethods(hash, parent, visitedClasses);
1239
1240         /* Java 5 or later allows covariant return types.
1241          * It also allows contra-variant parameter types
1242          * if a super class is a generics with concrete type arguments
1243          * such as Foo<String>.  So the method-overriding rule is complex.
1244          */

1245         Method[] methods = SecurityActions.getDeclaredMethods(clazz);
1246         for (int i = 0; i < methods.length; i++)
1247             if (!Modifier.isPrivate(methods[i].getModifiers())) {
1248                 Method m = methods[i];
1249                 String key = m.getName() + ':' + RuntimeSupport.makeDescriptor(m);  // see keyToDesc().
1250                 if (key.startsWith(HANDLER_GETTER_KEY))
1251                     hasGetHandler = true;
1252
1253                 // JIRA JASSIST-85
1254                 // put the method to the cache, retrieve previous definition (if any)
1255                 Method oldMethod = hash.put(key, m);
1256
1257                 // JIRA JASSIST-244, 267
1258                 // ignore a bridge method to a method declared in a non-public class.
1259                 if (null != oldMethod && isBridge(m)
1260                     && !Modifier.isPublic(oldMethod.getDeclaringClass().getModifiers())
1261                     && !Modifier.isAbstract(oldMethod.getModifiers()) && !isDuplicated(i, methods))
1262                     hash.put(key, oldMethod);
1263
1264                 // check if visibility has been reduced 
1265                 if (null != oldMethod && Modifier.isPublic(oldMethod.getModifiers())
1266                                       && !Modifier.isPublic(m.getModifiers())) { 
1267                     // we tried to overwrite a public definition with a non-public definition,
1268                     // use the old definition instead. 
1269                     hash.put(key, oldMethod); 
1270                 }
1271             }
1272     }
1273
1274     private static boolean isDuplicated(int index, Method[] methods) {
1275         String name = methods[index].getName();
1276         for (int i = 0; i < methods.length; i++)
1277             if (i != index)
1278                 if (name.equals(methods[i].getName()) && areParametersSame(methods[index], methods[i]))
1279                     return true;
1280
1281         return false;
1282     }
1283     
1284     private static boolean areParametersSame(Method method, Method targetMethod) {
1285         Class<?>[] methodTypes = method.getParameterTypes();
1286         Class<?>[] targetMethodTypes = targetMethod.getParameterTypes();
1287         if (methodTypes.length == targetMethodTypes.length) {
1288             for (int i = 0; i< methodTypes.length; i++) {
1289                 if (methodTypes[i].getName().equals(targetMethodTypes[i].getName())) {
1290                     continue;
1291                 } else {
1292                     return false;
1293                 }
1294             }
1295             return true;
1296         }
1297         return false;
1298     }
1299     
1300     private static final String HANDLER_GETTER_KEY
1301         = HANDLER_GETTER + ":()";
1302
1303     private static String keyToDesc(String key, Method m) {
1304         return key.substring(key.indexOf(':') + 1);
1305     }
1306
1307     private static MethodInfo makeConstructor(String thisClassName, Constructor<?> cons,
1308                                               ConstPool cp, Class<?> superClass, boolean doHandlerInit) {
1309         String desc = RuntimeSupport.makeDescriptor(cons.getParameterTypes(),
1310                                                     Void.TYPE);
1311         MethodInfo minfo = new MethodInfo(cp, "<init>", desc);
1312         minfo.setAccessFlags(Modifier.PUBLIC);      // cons.getModifiers() & ~Modifier.NATIVE
1313         setThrows(minfo, cp, cons.getExceptionTypes());
1314         Bytecode code = new Bytecode(cp, 0, 0);
1315
1316         // legacy: if we are not using caching then we initialise the instance's handler
1317         // from the class's static default interceptor and skip the next few instructions if
1318         // it is non-null
1319         if (doHandlerInit) {
1320             code.addAload(0);
1321             code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
1322             code.addPutfield(thisClassName, HANDLER, HANDLER_TYPE);
1323             code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
1324             code.addOpcode(Opcode.IFNONNULL);
1325             code.addIndex(10);
1326         }
1327         // if caching is enabled then we don't have a handler to initialise so this else branch will install
1328         // the handler located in the static field of class RuntimeSupport.
1329         code.addAload(0);
1330         code.addGetstatic(NULL_INTERCEPTOR_HOLDER, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
1331         code.addPutfield(thisClassName, HANDLER, HANDLER_TYPE);
1332         int pc = code.currentPc();
1333
1334         code.addAload(0);
1335         int s = addLoadParameters(code, cons.getParameterTypes(), 1);
1336         code.addInvokespecial(superClass.getName(), "<init>", desc);
1337         code.addOpcode(Opcode.RETURN);
1338         code.setMaxLocals(s + 1);
1339         CodeAttribute ca = code.toCodeAttribute();
1340         minfo.setCodeAttribute(ca);
1341
1342         StackMapTable.Writer writer = new StackMapTable.Writer(32);
1343         writer.sameFrame(pc);
1344         ca.setAttribute(writer.toStackMapTable(cp));
1345         return minfo;
1346     }
1347
1348     private MethodInfo makeDelegator(Method meth, String desc,
1349                 ConstPool cp, Class<?> declClass, String delegatorName) {
1350         MethodInfo delegator = new MethodInfo(cp, delegatorName, desc);
1351         delegator.setAccessFlags(Modifier.FINAL | Modifier.PUBLIC
1352                 | (meth.getModifiers() & ~(Modifier.PRIVATE
1353                                            | Modifier.PROTECTED
1354                                            | Modifier.ABSTRACT
1355                                            | Modifier.NATIVE
1356                                            | Modifier.SYNCHRONIZED)));
1357         setThrows(delegator, cp, meth);
1358         Bytecode code = new Bytecode(cp, 0, 0);
1359         code.addAload(0);
1360         int s = addLoadParameters(code, meth.getParameterTypes(), 1);
1361         Class<?> targetClass = invokespecialTarget(declClass);
1362         code.addInvokespecial(targetClass.isInterface(), cp.addClassInfo(targetClass.getName()),
1363                               meth.getName(), desc);
1364         addReturn(code, meth.getReturnType());
1365         code.setMaxLocals(++s);
1366         delegator.setCodeAttribute(code.toCodeAttribute());
1367         return delegator;
1368     }
1369
1370     /* Suppose that the receiver type is S, the invoked method
1371      * is declared in T, and U is the immediate super class of S
1372      * (or its interface).  If S <: U <: T (S <: T reads "S extends T"), 
1373      * the target type of invokespecial has to be not T but U.
1374      */

1375     private Class<?> invokespecialTarget(Class<?> declClass) {
1376         if (declClass.isInterface())
1377             for (Class<?> i: interfaces)
1378                 if (declClass.isAssignableFrom(i))
1379                     return i;
1380
1381         return superClass;
1382     }
1383
1384     /**
1385      * @param delegatorName     null if the original method is abstract.
1386      */

1387     private static MethodInfo makeForwarder(String thisClassName,
1388                     Method meth, String desc, ConstPool cp,
1389                     Class<?> declClass, String delegatorName, int index,
1390                     List<Find2MethodsArgs> forwarders) {
1391         MethodInfo forwarder = new MethodInfo(cp, meth.getName(), desc);
1392         forwarder.setAccessFlags(Modifier.FINAL
1393                     | (meth.getModifiers() & ~(Modifier.ABSTRACT
1394                                                | Modifier.NATIVE
1395                                                | Modifier.SYNCHRONIZED)));
1396         setThrows(forwarder, cp, meth);
1397         int args = Descriptor.paramSize(desc);
1398         Bytecode code = new Bytecode(cp, 0, args + 2);
1399         /*
1400          * static {
1401          *   methods[index * 2]
1402          *     = RuntimeSupport.findSuperMethod(this, <overridden name>, <desc>);
1403          *   methods[index * 2 + 1]
1404          *     = RuntimeSupport.findMethod(this, <delegator name>, <desc>);
1405          *     or = null // the original method is abstract.
1406          * }
1407          *     :
1408          * return ($r)handler.invoke(this, methods[index * 2],
1409          *                methods[index * 2 + 1], $args);
1410          */

1411         int origIndex = index * 2;
1412         int delIndex = index * 2 + 1;
1413         int arrayVar = args + 1;
1414         code.addGetstatic(thisClassName, HOLDER, HOLDER_TYPE);
1415         code.addAstore(arrayVar);
1416
1417         forwarders.add(new Find2MethodsArgs(meth.getName(), delegatorName, desc, origIndex));
1418
1419         code.addAload(0);
1420         code.addGetfield(thisClassName, HANDLER, HANDLER_TYPE);
1421         code.addAload(0);
1422
1423         code.addAload(arrayVar);
1424         code.addIconst(origIndex);
1425         code.addOpcode(Opcode.AALOAD);
1426
1427         code.addAload(arrayVar);
1428         code.addIconst(delIndex);
1429         code.addOpcode(Opcode.AALOAD);
1430
1431         makeParameterList(code, meth.getParameterTypes());
1432         code.addInvokeinterface(MethodHandler.class.getName(), "invoke",
1433             "(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;",
1434             5);
1435         Class<?> retType = meth.getReturnType();
1436         addUnwrapper(code, retType);
1437         addReturn(code, retType);
1438
1439         CodeAttribute ca = code.toCodeAttribute();
1440         forwarder.setCodeAttribute(ca);
1441         return forwarder;
1442     }
1443
1444     static class Find2MethodsArgs {
1445         String methodName, delegatorName, descriptor;
1446         int origIndex;
1447
1448         Find2MethodsArgs(String mname, String dname, String desc, int index) {
1449             methodName = mname;
1450             delegatorName = dname;
1451             descriptor = desc;
1452             origIndex = index;
1453         }
1454     }
1455
1456     private static void setThrows(MethodInfo minfo, ConstPool cp, Method orig) {
1457         Class<?>[] exceptions = orig.getExceptionTypes();
1458         setThrows(minfo, cp, exceptions);
1459     }
1460
1461     private static void setThrows(MethodInfo minfo, ConstPool cp,
1462                                   Class<?>[] exceptions) {
1463         if (exceptions.length == 0)
1464             return;
1465
1466         String[] list = new String[exceptions.length];
1467         for (int i = 0; i < exceptions.length; i++)
1468             list[i] = exceptions[i].getName();
1469
1470         ExceptionsAttribute ea = new ExceptionsAttribute(cp);
1471         ea.setExceptions(list);
1472         minfo.setExceptionsAttribute(ea);
1473     }
1474
1475     private static int addLoadParameters(Bytecode code, Class<?>[] params,
1476                                          int offset) {
1477         int stacksize = 0;
1478         int n = params.length;
1479         for (int i = 0; i < n; ++i)
1480             stacksize += addLoad(code, stacksize + offset, params[i]);
1481
1482         return stacksize;
1483     }
1484
1485     private static int addLoad(Bytecode code, int n, Class<?> type) {
1486         if (type.isPrimitive()) {
1487             if (type == Long.TYPE) {
1488                 code.addLload(n);
1489                 return 2;
1490             }
1491             else if (type == Float.TYPE)
1492                 code.addFload(n);
1493             else if (type == Double.TYPE) {
1494                 code.addDload(n);
1495                 return 2;
1496             }
1497             else
1498                 code.addIload(n);
1499         }
1500         else
1501             code.addAload(n);
1502
1503         return 1;
1504     }
1505
1506     private static int addReturn(Bytecode code, Class<?> type) {
1507         if (type.isPrimitive()) {
1508             if (type == Long.TYPE) {
1509                 code.addOpcode(Opcode.LRETURN);
1510                 return 2;
1511             }
1512             else if (type == Float.TYPE)
1513                 code.addOpcode(Opcode.FRETURN);
1514             else if (type == Double.TYPE) {
1515                 code.addOpcode(Opcode.DRETURN);
1516                 return 2;
1517             }
1518             else if (type == Void.TYPE) {
1519                 code.addOpcode(Opcode.RETURN);
1520                 return 0;
1521             }
1522             else
1523                 code.addOpcode(Opcode.IRETURN);
1524         }
1525         else
1526             code.addOpcode(Opcode.ARETURN);
1527
1528         return 1;
1529     }
1530
1531     private static void makeParameterList(Bytecode code, Class<?>[] params) {
1532         int regno = 1;
1533         int n = params.length;
1534         code.addIconst(n);
1535         code.addAnewarray("java/lang/Object");
1536         for (int i = 0; i < n; i++) {
1537             code.addOpcode(Opcode.DUP);
1538             code.addIconst(i);
1539             Class<?> type = params[i];
1540             if (type.isPrimitive())
1541                 regno = makeWrapper(code, type, regno);
1542             else {
1543                 code.addAload(regno);
1544                 regno++;
1545             }
1546
1547             code.addOpcode(Opcode.AASTORE);
1548         }
1549     }
1550
1551     private static int makeWrapper(Bytecode code, Class<?> type, int regno) {
1552         int index = FactoryHelper.typeIndex(type);
1553         String wrapper = FactoryHelper.wrapperTypes[index]; 
1554         code.addNew(wrapper);
1555         code.addOpcode(Opcode.DUP);
1556         addLoad(code, regno, type);
1557         code.addInvokespecial(wrapper, "<init>",
1558                               FactoryHelper.wrapperDesc[index]);
1559         return regno + FactoryHelper.dataSize[index];
1560     }
1561
1562     private static void addUnwrapper(Bytecode code, Class<?> type) {
1563         if (type.isPrimitive()) {
1564             if (type == Void.TYPE)
1565                 code.addOpcode(Opcode.POP);
1566             else {
1567                 int index = FactoryHelper.typeIndex(type);
1568                 String wrapper = FactoryHelper.wrapperTypes[index];
1569                 code.addCheckcast(wrapper);
1570                 code.addInvokevirtual(wrapper,
1571                                       FactoryHelper.unwarpMethods[index],
1572                                       FactoryHelper.unwrapDesc[index]);
1573             }
1574         }
1575         else       
1576             code.addCheckcast(type.getName());
1577     }
1578
1579     private static MethodInfo makeWriteReplace(ConstPool cp) {
1580         MethodInfo minfo = new MethodInfo(cp, "writeReplace""()Ljava/lang/Object;");
1581         String[] list = new String[1];
1582         list[0] = "java.io.ObjectStreamException";
1583         ExceptionsAttribute ea = new ExceptionsAttribute(cp);
1584         ea.setExceptions(list);
1585         minfo.setExceptionsAttribute(ea);
1586         Bytecode code = new Bytecode(cp, 0, 1);
1587         code.addAload(0);
1588         code.addInvokestatic("javassist.util.proxy.RuntimeSupport",
1589                              "makeSerializedProxy",
1590                              "(Ljava/lang/Object;)Ljavassist/util/proxy/SerializedProxy;");
1591         code.addOpcode(Opcode.ARETURN);
1592         minfo.setCodeAttribute(code.toCodeAttribute());
1593         return minfo;
1594     }
1595 }
1596