• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
isUseCache()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      */
setUseCache(boolean useCache)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      */
isUseWriteReplace()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      */
setUseWriteReplace(boolean useWriteReplace)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      */
isProxyClass(Class<?> cl)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 
ProxyDetails(byte[] signature, Class<?> proxyClass, boolean isUseWriteReplace)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      */
ProxyFactory()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      */
setSuperclass(Class<?> clazz)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      */
getSuperclass()411     public Class<?> getSuperclass() { return superClass; }
412 
413     /**
414      * Sets the interfaces of a proxy class.
415      */
setInterfaces(Class<?>[] ifs)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      */
getInterfaces()427     public Class<?>[] getInterfaces() { return interfaces; }
428 
429     /**
430      * Sets a filter that selects the methods that will be controlled by a handler.
431      */
setFilter(MethodFilter mf)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      */
createClass()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      */
createClass(MethodFilter filter)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      */
createClass(byte[] signature)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      */
createClass(Lookup lookup)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      */
createClass(Lookup lookup, MethodFilter filter)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      */
createClass(Lookup lookup, byte[] signature)511     Class<?> createClass(Lookup lookup, byte[] signature)
512     {
513         installSignature(signature);
514         return createClass1(lookup);
515     }
516 
createClass1(Lookup lookup)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 
getKey(Class<?> superClass, Class<?>[] interfaces, byte[] signature, boolean useWriteReplace)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 
createClass2(ClassLoader cl, Lookup lookup)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 
createClass3(ClassLoader cl, Lookup lookup)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      */
getClassInTheSamePackage()624     private Class<?> getClassInTheSamePackage() {
625         if (basename.startsWith(packageForJavaBase))       // maybe the super class is java.*
626             return this.getClass();
627         else if (superClass != null && superClass != OBJECT_TYPE)
628             return superClass;
629         else if (interfaces != null && interfaces.length > 0)
630             return interfaces[0];
631         else
632             return this.getClass();     // maybe wrong?
633     }
634 
setField(String fieldName, Object value)635     private void setField(String fieldName, Object value) {
636         if (thisClass != null && value != null)
637             try {
638                 Field f = thisClass.getField(fieldName);
639                 SecurityActions.setAccessible(f, true);
640                 f.set(null, value);
641                 SecurityActions.setAccessible(f, false);
642             }
643             catch (Exception e) {
644                 throw new RuntimeException(e);
645             }
646     }
647 
getFilterSignature(Class<?> clazz)648     static byte[] getFilterSignature(Class<?> clazz) {
649         return (byte[])getField(clazz, FILTER_SIGNATURE_FIELD);
650     }
651 
getField(Class<?> clazz, String fieldName)652     private static Object getField(Class<?> clazz, String fieldName) {
653         try {
654             Field f = clazz.getField(fieldName);
655             f.setAccessible(true);
656             Object value = f.get(null);
657             f.setAccessible(false);
658             return value;
659         }
660         catch (Exception e) {
661             throw new RuntimeException(e);
662         }
663     }
664 
665     /**
666      * Obtains the method handler of the given proxy object.
667      *
668      * @param p     a proxy object.
669      * @return the method handler.
670      * @since 3.16
671      */
getHandler(Proxy p)672     public static MethodHandler getHandler(Proxy p) {
673         try {
674             Field f = p.getClass().getDeclaredField(HANDLER);
675             f.setAccessible(true);
676             Object value = f.get(p);
677             f.setAccessible(false);
678             return (MethodHandler)value;
679         }
680         catch (Exception e) {
681             throw new RuntimeException(e);
682         }
683     }
684 
685     /**
686      * A provider of class loaders.
687      *
688      * @see #classLoaderProvider
689      * @since 3.4
690      */
691     public static interface ClassLoaderProvider {
692         /**
693          * Returns a class loader.
694          *
695          * @param pf    a proxy factory that is going to obtain a class loader.
696          */
get(ProxyFactory pf)697         public ClassLoader get(ProxyFactory pf);
698     }
699 
700     /**
701      * A provider used by <code>createClass()</code> for obtaining
702      * a class loader.
703      * <code>get()</code> on this <code>ClassLoaderProvider</code> object
704      * is called to obtain a class loader.
705      *
706      * <p>The value of this field can be updated for changing the default
707      * implementation.
708      *
709      * <p>Example:
710      * <pre>
711      * ProxyFactory.classLoaderProvider = new ProxyFactory.ClassLoaderProvider() {
712      *     public ClassLoader get(ProxyFactory pf) {
713      *         return Thread.currentThread().getContextClassLoader();
714      *     }
715      * };
716      * </pre>
717      *
718      * @since 3.4
719      */
720     public static ClassLoaderProvider classLoaderProvider =
721         new ClassLoaderProvider() {
722             @Override
723             public ClassLoader get(ProxyFactory pf) {
724                   return pf.getClassLoader0();
725               }
726         };
727 
getClassLoader()728     protected ClassLoader getClassLoader() {
729         return classLoaderProvider.get(this);
730     }
731 
getClassLoader0()732     protected ClassLoader getClassLoader0() {
733         ClassLoader loader = null;
734         if (superClass != null && !superClass.getName().equals("java.lang.Object"))
735             loader = superClass.getClassLoader();
736         else if (interfaces != null && interfaces.length > 0)
737             loader = interfaces[0].getClassLoader();
738 
739         if (loader == null) {
740             loader = getClass().getClassLoader();
741             // In case javassist is in the endorsed dir
742             if (loader == null) {
743                 loader = Thread.currentThread().getContextClassLoader();
744                 if (loader == null)
745                     loader = ClassLoader.getSystemClassLoader();
746             }
747         }
748 
749         return loader;
750     }
751 
getDomain()752     protected ProtectionDomain getDomain() {
753         Class<?> clazz;
754         if (superClass != null && !superClass.getName().equals("java.lang.Object"))
755             clazz = superClass;
756         else if (interfaces != null && interfaces.length > 0)
757             clazz = interfaces[0];
758         else
759             clazz = this.getClass();
760 
761         return clazz.getProtectionDomain();
762     }
763 
764     /**
765      * Creates a proxy class and returns an instance of that class.
766      *
767      * @param paramTypes    parameter types for a constructor.
768      * @param args          arguments passed to a constructor.
769      * @param mh            the method handler for the proxy class.
770      * @since 3.4
771      */
create(Class<?>[] paramTypes, Object[] args, MethodHandler mh)772     public Object create(Class<?>[] paramTypes, Object[] args, MethodHandler mh)
773         throws NoSuchMethodException, IllegalArgumentException,
774                InstantiationException, IllegalAccessException, InvocationTargetException
775     {
776         Object obj = create(paramTypes, args);
777         ((Proxy)obj).setHandler(mh);
778         return obj;
779     }
780 
781     /**
782      * Creates a proxy class and returns an instance of that class.
783      *
784      * @param paramTypes    parameter types for a constructor.
785      * @param args          arguments passed to a constructor.
786      */
create(Class<?>[] paramTypes, Object[] args)787     public Object create(Class<?>[] paramTypes, Object[] args)
788         throws NoSuchMethodException, IllegalArgumentException,
789                InstantiationException, IllegalAccessException, InvocationTargetException
790     {
791         Class<?> c = createClass();
792         Constructor<?> cons = c.getConstructor(paramTypes);
793         return cons.newInstance(args);
794     }
795 
796     /**
797      * Sets the default invocation handler.  This invocation handler is shared
798      * among all the instances of a proxy class unless another is explicitly
799      * specified.
800      * @deprecated since 3.12
801      * use of this method is incompatible  with proxy class caching.
802      * instead clients should call method {@link Proxy#setHandler(MethodHandler)} to set the handler
803      * for each newly created  proxy instance.
804      * calling this method will automatically disable caching of classes created by the proxy factory.
805      */
806     @Deprecated
setHandler(MethodHandler mi)807     public void setHandler(MethodHandler mi) {
808         // if we were using the cache and the handler is non-null then we must stop caching
809         if (factoryUseCache && mi != null)  {
810             factoryUseCache = false;
811             // clear any currently held class so we don't try to reuse it or set its handler field
812           thisClass  = null;
813         }
814         handler = mi;
815         // this retains the behaviour of the old code which resets any class we were holding on to
816         // this is probably not what is wanted
817         setField(DEFAULT_INTERCEPTOR, handler);
818     }
819 
820     /**
821      * A unique class name generator.
822      */
823     public static interface UniqueName {
824         /**
825          * Returns a unique class name.
826          *
827          * @param classname     the super class name of the proxy class.
828          */
get(String classname)829         String get(String classname);
830     }
831 
832     /**
833      * A unique class name generator.
834      * Replacing this generator changes the algorithm to generate a
835      * unique name. The <code>get</code> method does not have to be
836      * a <code>synchronized</code> method since the access to this field
837      * is mutually exclusive and thus thread safe.
838      */
839     public static UniqueName nameGenerator = new UniqueName() {
840         private final String sep = "_$$_jvst" + Integer.toHexString(this.hashCode() & 0xfff) + "_";
841         private int counter = 0;
842 
843         @Override
844         public String get(String classname) {
845             return classname + sep + Integer.toHexString(counter++);
846         }
847     };
848 
makeProxyName(String classname)849     private static String makeProxyName(String classname) {
850         synchronized (nameGenerator) {
851             return nameGenerator.get(classname);
852         }
853     }
854 
make()855     private ClassFile make() throws CannotCompileException {
856         ClassFile cf = new ClassFile(false, classname, superName);
857         cf.setAccessFlags(AccessFlag.PUBLIC);
858         setInterfaces(cf, interfaces, hasGetHandler ? Proxy.class : ProxyObject.class);
859         ConstPool pool = cf.getConstPool();
860 
861         // legacy: we only add the static field for the default interceptor if caching is disabled
862         if  (!factoryUseCache) {
863             FieldInfo finfo = new FieldInfo(pool, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
864             finfo.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
865             cf.addField(finfo);
866         }
867 
868         // handler is per instance
869         FieldInfo finfo2 = new FieldInfo(pool, HANDLER, HANDLER_TYPE);
870         finfo2.setAccessFlags(AccessFlag.PRIVATE);
871         cf.addField(finfo2);
872 
873         // filter signature is per class
874         FieldInfo finfo3 = new FieldInfo(pool, FILTER_SIGNATURE_FIELD, FILTER_SIGNATURE_TYPE);
875         finfo3.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
876         cf.addField(finfo3);
877 
878         // the proxy class serial uid must always be a fixed value
879         FieldInfo finfo4 = new FieldInfo(pool, SERIAL_VERSION_UID_FIELD, SERIAL_VERSION_UID_TYPE);
880         finfo4.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC| AccessFlag.FINAL);
881         cf.addField(finfo4);
882 
883         // HashMap allMethods = getMethods(superClass, interfaces);
884         // int size = allMethods.size();
885         makeConstructors(classname, cf, pool, classname);
886 
887         List<Find2MethodsArgs> forwarders = new ArrayList<Find2MethodsArgs>();
888         int s = overrideMethods(cf, pool, classname, forwarders);
889         addClassInitializer(cf, pool, classname, s, forwarders);
890         addSetter(classname, cf, pool);
891         if (!hasGetHandler)
892             addGetter(classname, cf, pool);
893 
894         if (factoryWriteReplace) {
895             try {
896                 cf.addMethod(makeWriteReplace(pool));
897             }
898             catch (DuplicateMemberException e) {
899                 // writeReplace() is already declared in the super class/interfaces.
900             }
901         }
902 
903         thisClass = null;
904         return cf;
905     }
906 
checkClassAndSuperName()907     private void checkClassAndSuperName() {
908         if (interfaces == null)
909             interfaces = new Class[0];
910 
911         if (superClass == null) {
912             superClass = OBJECT_TYPE;
913             superName = superClass.getName();
914             basename = interfaces.length == 0 ? superName
915                                               : interfaces[0].getName();
916         } else {
917             superName = superClass.getName();
918             basename = superName;
919         }
920 
921         if (Modifier.isFinal(superClass.getModifiers()))
922             throw new RuntimeException(superName + " is final");
923 
924         // Since java.base module is not opened, its proxy class should be
925         // in a different (open) module.  Otherwise, it could not be created
926         // by reflection.
927         if (basename.startsWith("java.") || basename.startsWith("jdk.") || onlyPublicMethods)
928             basename = packageForJavaBase + basename.replace('.', '_');
929     }
930 
931     private static final String packageForJavaBase = "javassist.util.proxy.";
932 
allocateClassName()933     private void allocateClassName() {
934         classname = makeProxyName(basename);
935     }
936 
937     private static Comparator<Map.Entry<String,Method>> sorter =
938         new Comparator<Map.Entry<String,Method>>() {
939             @Override
940             public int compare(Map.Entry<String,Method> e1, Map.Entry<String,Method> e2) {
941                 return e1.getKey().compareTo(e2.getKey());
942             }
943         };
944 
makeSortedMethodList()945     private void makeSortedMethodList() {
946         checkClassAndSuperName();
947 
948         hasGetHandler = false;      // getMethods() may set this to true.
949         Map<String,Method> allMethods = getMethods(superClass, interfaces);
950         signatureMethods = new ArrayList<Map.Entry<String,Method>>(allMethods.entrySet());
951         Collections.sort(signatureMethods, sorter);
952     }
953 
computeSignature(MethodFilter filter)954     private void computeSignature(MethodFilter filter) // throws CannotCompileException
955     {
956         makeSortedMethodList();
957 
958         int l = signatureMethods.size();
959         int maxBytes = ((l + 7) >> 3);
960         signature = new byte[maxBytes];
961         for (int idx = 0; idx < l; idx++)
962         {
963             Method m = signatureMethods.get(idx).getValue();
964             int mod = m.getModifiers();
965             if (!Modifier.isFinal(mod) && !Modifier.isStatic(mod)
966                     && isVisible(mod, basename, m) && (filter == null || filter.isHandled(m))) {
967                 setBit(signature, idx);
968             }
969         }
970     }
971 
installSignature(byte[] signature)972     private void installSignature(byte[] signature) // throws CannotCompileException
973     {
974         makeSortedMethodList();
975 
976         int l = signatureMethods.size();
977         int maxBytes = ((l + 7) >> 3);
978         if (signature.length != maxBytes) {
979             throw new RuntimeException("invalid filter signature length for deserialized proxy class");
980         }
981 
982         this.signature =  signature;
983     }
984 
testBit(byte[] signature, int idx)985     private boolean testBit(byte[] signature, int idx) {
986         int byteIdx = idx >> 3;
987         if (byteIdx > signature.length)
988             return false;
989         int bitIdx = idx & 0x7;
990         int mask = 0x1 << bitIdx;
991         int sigByte = signature[byteIdx];
992         return ((sigByte & mask) != 0);
993     }
994 
setBit(byte[] signature, int idx)995     private void setBit(byte[] signature, int idx) {
996         int byteIdx = idx >> 3;
997         if (byteIdx < signature.length) {
998             int bitIdx = idx & 0x7;
999             int mask = 0x1 << bitIdx;
1000             int sigByte = signature[byteIdx];
1001             signature[byteIdx] = (byte)(sigByte | mask);
1002         }
1003     }
1004 
setInterfaces(ClassFile cf, Class<?>[] interfaces, Class<?> proxyClass)1005     private static void setInterfaces(ClassFile cf, Class<?>[] interfaces, Class<?> proxyClass) {
1006         String setterIntf = proxyClass.getName();
1007         String[] list;
1008         if (interfaces == null || interfaces.length == 0)
1009             list = new String[] { setterIntf };
1010         else {
1011             list = new String[interfaces.length + 1];
1012             for (int i = 0; i < interfaces.length; i++)
1013                 list[i] = interfaces[i].getName();
1014 
1015             list[interfaces.length] = setterIntf;
1016         }
1017 
1018         cf.setInterfaces(list);
1019     }
1020 
addClassInitializer(ClassFile cf, ConstPool cp, String classname, int size, List<Find2MethodsArgs> forwarders)1021     private static void addClassInitializer(ClassFile cf, ConstPool cp,
1022                 String classname, int size, List<Find2MethodsArgs> forwarders)
1023         throws CannotCompileException
1024     {
1025         FieldInfo finfo = new FieldInfo(cp, HOLDER, HOLDER_TYPE);
1026         finfo.setAccessFlags(AccessFlag.PRIVATE | AccessFlag.STATIC);
1027         cf.addField(finfo);
1028         MethodInfo minfo = new MethodInfo(cp, "<clinit>", "()V");
1029         minfo.setAccessFlags(AccessFlag.STATIC);
1030         setThrows(minfo, cp, new Class<?>[] { ClassNotFoundException.class });
1031 
1032         Bytecode code = new Bytecode(cp, 0, 2);
1033         code.addIconst(size * 2);
1034         code.addAnewarray("java.lang.reflect.Method");
1035         final int varArray = 0;
1036         code.addAstore(varArray);
1037 
1038         // forName() must be called here.  Otherwise, the class might be
1039         // invisible.
1040         code.addLdc(classname);
1041         code.addInvokestatic("java.lang.Class",
1042                 "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
1043         final int varClass = 1;
1044         code.addAstore(varClass);
1045 
1046         for (Find2MethodsArgs args:forwarders)
1047             callFind2Methods(code, args.methodName, args.delegatorName,
1048                              args.origIndex, args.descriptor, varClass, varArray);
1049 
1050         code.addAload(varArray);
1051         code.addPutstatic(classname, HOLDER, HOLDER_TYPE);
1052 
1053         code.addLconst(SERIAL_VERSION_UID_VALUE);
1054         code.addPutstatic(classname, SERIAL_VERSION_UID_FIELD, SERIAL_VERSION_UID_TYPE);
1055         code.addOpcode(Bytecode.RETURN);
1056         minfo.setCodeAttribute(code.toCodeAttribute());
1057         cf.addMethod(minfo);
1058     }
1059 
1060     /**
1061      * @param thisMethod        might be null.
1062      */
callFind2Methods(Bytecode code, String superMethod, String thisMethod, int index, String desc, int classVar, int arrayVar)1063     private static void callFind2Methods(Bytecode code, String superMethod, String thisMethod,
1064                                          int index, String desc, int classVar, int arrayVar) {
1065         String findClass = RuntimeSupport.class.getName();
1066         String findDesc
1067             = "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;[Ljava/lang/reflect/Method;)V";
1068 
1069         code.addAload(classVar);
1070         code.addLdc(superMethod);
1071         if (thisMethod == null)
1072             code.addOpcode(Opcode.ACONST_NULL);
1073         else
1074             code.addLdc(thisMethod);
1075 
1076         code.addIconst(index);
1077         code.addLdc(desc);
1078         code.addAload(arrayVar);
1079         code.addInvokestatic(findClass, "find2Methods", findDesc);
1080     }
1081 
addSetter(String classname, ClassFile cf, ConstPool cp)1082     private static void addSetter(String classname, ClassFile cf, ConstPool cp)
1083         throws CannotCompileException
1084     {
1085         MethodInfo minfo = new MethodInfo(cp, HANDLER_SETTER,
1086                                           HANDLER_SETTER_TYPE);
1087         minfo.setAccessFlags(AccessFlag.PUBLIC);
1088         Bytecode code = new Bytecode(cp, 2, 2);
1089         code.addAload(0);
1090         code.addAload(1);
1091         code.addPutfield(classname, HANDLER, HANDLER_TYPE);
1092         code.addOpcode(Bytecode.RETURN);
1093         minfo.setCodeAttribute(code.toCodeAttribute());
1094         cf.addMethod(minfo);
1095     }
1096 
addGetter(String classname, ClassFile cf, ConstPool cp)1097     private static void addGetter(String classname, ClassFile cf, ConstPool cp)
1098         throws CannotCompileException
1099     {
1100         MethodInfo minfo = new MethodInfo(cp, HANDLER_GETTER,
1101                                           HANDLER_GETTER_TYPE);
1102         minfo.setAccessFlags(AccessFlag.PUBLIC);
1103         Bytecode code = new Bytecode(cp, 1, 1);
1104         code.addAload(0);
1105         code.addGetfield(classname, HANDLER, HANDLER_TYPE);
1106         code.addOpcode(Bytecode.ARETURN);
1107         minfo.setCodeAttribute(code.toCodeAttribute());
1108         cf.addMethod(minfo);
1109     }
1110 
overrideMethods(ClassFile cf, ConstPool cp, String className, List<Find2MethodsArgs> forwarders)1111     private int overrideMethods(ClassFile cf, ConstPool cp, String className, List<Find2MethodsArgs> forwarders)
1112         throws CannotCompileException
1113     {
1114         String prefix = makeUniqueName("_d", signatureMethods);
1115         Iterator<Map.Entry<String,Method>> it = signatureMethods.iterator();
1116         int index = 0;
1117         while (it.hasNext()) {
1118             Map.Entry<String,Method> e = it.next();
1119             if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_5 || !isBridge(e.getValue()))
1120             	if (testBit(signature, index)) {
1121             		override(className, e.getValue(), prefix, index,
1122             				 keyToDesc(e.getKey(), e.getValue()), cf, cp, forwarders);
1123             	}
1124 
1125             index++;
1126         }
1127 
1128         return index;
1129     }
1130 
isBridge(Method m)1131     private static boolean isBridge(Method m) {
1132     	return m.isBridge();
1133     }
1134 
override(String thisClassname, Method meth, String prefix, int index, String desc, ClassFile cf, ConstPool cp, List<Find2MethodsArgs> forwarders)1135     private void override(String thisClassname, Method meth, String prefix,
1136                           int index, String desc, ClassFile cf, ConstPool cp,
1137                           List<Find2MethodsArgs> forwarders)
1138         throws CannotCompileException
1139     {
1140         Class<?> declClass = meth.getDeclaringClass();
1141         String delegatorName = prefix + index + meth.getName();
1142         if (Modifier.isAbstract(meth.getModifiers()))
1143             delegatorName = null;
1144         else {
1145             MethodInfo delegator
1146                 = makeDelegator(meth, desc, cp, declClass, delegatorName);
1147             // delegator is not a bridge method.  See Sec. 15.12.4.5 of JLS 3rd Ed.
1148             delegator.setAccessFlags(delegator.getAccessFlags() & ~AccessFlag.BRIDGE);
1149             cf.addMethod(delegator);
1150         }
1151 
1152         MethodInfo forwarder
1153             = makeForwarder(thisClassname, meth, desc, cp, declClass,
1154                             delegatorName, index, forwarders);
1155         cf.addMethod(forwarder);
1156     }
1157 
makeConstructors(String thisClassName, ClassFile cf, ConstPool cp, String classname)1158     private void makeConstructors(String thisClassName, ClassFile cf,
1159             ConstPool cp, String classname) throws CannotCompileException
1160     {
1161         Constructor<?>[] cons = SecurityActions.getDeclaredConstructors(superClass);
1162         // legacy: if we are not caching then we need to initialise the default handler
1163         boolean doHandlerInit = !factoryUseCache;
1164         for (int i = 0; i < cons.length; i++) {
1165             Constructor<?> c = cons[i];
1166             int mod = c.getModifiers();
1167             if (!Modifier.isFinal(mod) && !Modifier.isPrivate(mod)
1168                     && isVisible(mod, basename, c)) {
1169                 MethodInfo m = makeConstructor(thisClassName, c, cp, superClass, doHandlerInit);
1170                 cf.addMethod(m);
1171             }
1172         }
1173     }
1174 
makeUniqueName(String name, List<Map.Entry<String,Method>> sortedMethods)1175     private static String makeUniqueName(String name, List<Map.Entry<String,Method>> sortedMethods) {
1176         if (makeUniqueName0(name, sortedMethods.iterator()))
1177             return name;
1178 
1179         for (int i = 100; i < 999; i++) {
1180             String s = name + i;
1181             if (makeUniqueName0(s, sortedMethods.iterator()))
1182                 return s;
1183         }
1184 
1185         throw new RuntimeException("cannot make a unique method name");
1186     }
1187 
makeUniqueName0(String name, Iterator<Map.Entry<String,Method>> it)1188     private static boolean makeUniqueName0(String name, Iterator<Map.Entry<String,Method>> it) {
1189         while (it.hasNext())
1190             if (it.next().getKey().startsWith(name))
1191                 return false;
1192         return true;
1193     }
1194 
1195     /**
1196      * Returns true if the method is visible from the package.
1197      *
1198      * @param mod       the modifiers of the method.
1199      */
isVisible(int mod, String from, Member meth)1200     private static boolean isVisible(int mod, String from, Member meth) {
1201         if ((mod & Modifier.PRIVATE) != 0)
1202             return false;
1203         else if ((mod & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0)
1204             return true;
1205         else {
1206             String p = getPackageName(from);
1207             String q = getPackageName(meth.getDeclaringClass().getName());
1208             if (p == null)
1209                 return q == null;
1210             return p.equals(q);
1211         }
1212     }
1213 
getPackageName(String name)1214     private static String getPackageName(String name) {
1215         int i = name.lastIndexOf('.');
1216         if (i < 0)
1217             return null;
1218         return name.substring(0, i);
1219     }
1220 
1221     /* getMethods() may set hasGetHandler to true.
1222      */
getMethods(Class<?> superClass, Class<?>[] interfaceTypes)1223     private Map<String,Method> getMethods(Class<?> superClass, Class<?>[] interfaceTypes) {
1224         Map<String,Method> hash = new HashMap<String,Method>();
1225         Set<Class<?>> set = new HashSet<Class<?>>();
1226         for (int i = 0; i < interfaceTypes.length; i++)
1227             getMethods(hash, interfaceTypes[i], set);
1228 
1229         getMethods(hash, superClass, set);
1230         return hash;
1231     }
1232 
getMethods(Map<String,Method> hash, Class<?> clazz, Set<Class<?>> visitedClasses)1233     private void getMethods(Map<String,Method> hash, Class<?> clazz, Set<Class<?>> visitedClasses) {
1234         // This both speeds up scanning by avoiding duplicate interfaces and is needed to
1235         // ensure that superinterfaces are always scanned before subinterfaces.
1236         if (!visitedClasses.add(clazz))
1237             return;
1238 
1239         Class<?>[] ifs = clazz.getInterfaces();
1240         for (int i = 0; i < ifs.length; i++)
1241             getMethods(hash, ifs[i], visitedClasses);
1242 
1243         Class<?> parent = clazz.getSuperclass();
1244         if (parent != null)
1245             getMethods(hash, parent, visitedClasses);
1246 
1247         /* Java 5 or later allows covariant return types.
1248          * It also allows contra-variant parameter types
1249          * if a super class is a generics with concrete type arguments
1250          * such as Foo<String>.  So the method-overriding rule is complex.
1251          */
1252         Method[] methods = SecurityActions.getDeclaredMethods(clazz);
1253         for (int i = 0; i < methods.length; i++)
1254             if (!Modifier.isPrivate(methods[i].getModifiers())) {
1255                 Method m = methods[i];
1256                 String key = m.getName() + ':' + RuntimeSupport.makeDescriptor(m);  // see keyToDesc().
1257                 if (key.startsWith(HANDLER_GETTER_KEY))
1258                     hasGetHandler = true;
1259 
1260                 // JIRA JASSIST-85
1261                 // put the method to the cache, retrieve previous definition (if any)
1262                 Method oldMethod = hash.put(key, m);
1263 
1264                 // JIRA JASSIST-244, 267
1265                 // ignore a bridge method to a method declared in a non-public class.
1266                 if (null != oldMethod && isBridge(m)
1267                     && !Modifier.isPublic(oldMethod.getDeclaringClass().getModifiers())
1268                     && !Modifier.isAbstract(oldMethod.getModifiers()) && !isDuplicated(i, methods))
1269                     hash.put(key, oldMethod);
1270 
1271                 // check if visibility has been reduced
1272                 if (null != oldMethod && Modifier.isPublic(oldMethod.getModifiers())
1273                                       && !Modifier.isPublic(m.getModifiers())) {
1274                     // we tried to overwrite a public definition with a non-public definition,
1275                     // use the old definition instead.
1276                     hash.put(key, oldMethod);
1277                 }
1278             }
1279     }
1280 
isDuplicated(int index, Method[] methods)1281     private static boolean isDuplicated(int index, Method[] methods) {
1282         String name = methods[index].getName();
1283         for (int i = 0; i < methods.length; i++)
1284             if (i != index)
1285                 if (name.equals(methods[i].getName()) && areParametersSame(methods[index], methods[i]))
1286                     return true;
1287 
1288         return false;
1289     }
1290 
areParametersSame(Method method, Method targetMethod)1291     private static boolean areParametersSame(Method method, Method targetMethod) {
1292         Class<?>[] methodTypes = method.getParameterTypes();
1293         Class<?>[] targetMethodTypes = targetMethod.getParameterTypes();
1294         if (methodTypes.length == targetMethodTypes.length) {
1295             for (int i = 0; i< methodTypes.length; i++) {
1296                 if (methodTypes[i].getName().equals(targetMethodTypes[i].getName())) {
1297                     continue;
1298                 } else {
1299                     return false;
1300                 }
1301             }
1302             return true;
1303         }
1304         return false;
1305     }
1306 
1307     private static final String HANDLER_GETTER_KEY
1308     	= HANDLER_GETTER + ":()";
1309 
keyToDesc(String key, Method m)1310     private static String keyToDesc(String key, Method m) {
1311         return key.substring(key.indexOf(':') + 1);
1312     }
1313 
makeConstructor(String thisClassName, Constructor<?> cons, ConstPool cp, Class<?> superClass, boolean doHandlerInit)1314     private static MethodInfo makeConstructor(String thisClassName, Constructor<?> cons,
1315                                               ConstPool cp, Class<?> superClass, boolean doHandlerInit) {
1316         String desc = RuntimeSupport.makeDescriptor(cons.getParameterTypes(),
1317                                                     Void.TYPE);
1318         MethodInfo minfo = new MethodInfo(cp, "<init>", desc);
1319         minfo.setAccessFlags(Modifier.PUBLIC);      // cons.getModifiers() & ~Modifier.NATIVE
1320         setThrows(minfo, cp, cons.getExceptionTypes());
1321         Bytecode code = new Bytecode(cp, 0, 0);
1322 
1323         // legacy: if we are not using caching then we initialise the instance's handler
1324         // from the class's static default interceptor and skip the next few instructions if
1325         // it is non-null
1326         if (doHandlerInit) {
1327             code.addAload(0);
1328             code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
1329             code.addPutfield(thisClassName, HANDLER, HANDLER_TYPE);
1330             code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
1331             code.addOpcode(Opcode.IFNONNULL);
1332             code.addIndex(10);
1333         }
1334         // if caching is enabled then we don't have a handler to initialise so this else branch will install
1335         // the handler located in the static field of class RuntimeSupport.
1336         code.addAload(0);
1337         code.addGetstatic(NULL_INTERCEPTOR_HOLDER, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
1338         code.addPutfield(thisClassName, HANDLER, HANDLER_TYPE);
1339         int pc = code.currentPc();
1340 
1341         code.addAload(0);
1342         int s = addLoadParameters(code, cons.getParameterTypes(), 1);
1343         code.addInvokespecial(superClass.getName(), "<init>", desc);
1344         code.addOpcode(Opcode.RETURN);
1345         code.setMaxLocals(s + 1);
1346         CodeAttribute ca = code.toCodeAttribute();
1347         minfo.setCodeAttribute(ca);
1348 
1349         StackMapTable.Writer writer = new StackMapTable.Writer(32);
1350         writer.sameFrame(pc);
1351         ca.setAttribute(writer.toStackMapTable(cp));
1352         return minfo;
1353     }
1354 
makeDelegator(Method meth, String desc, ConstPool cp, Class<?> declClass, String delegatorName)1355     private MethodInfo makeDelegator(Method meth, String desc,
1356                 ConstPool cp, Class<?> declClass, String delegatorName) {
1357         MethodInfo delegator = new MethodInfo(cp, delegatorName, desc);
1358         delegator.setAccessFlags(Modifier.FINAL | Modifier.PUBLIC
1359                 | (meth.getModifiers() & ~(Modifier.PRIVATE
1360                                            | Modifier.PROTECTED
1361                                            | Modifier.ABSTRACT
1362                                            | Modifier.NATIVE
1363                                            | Modifier.SYNCHRONIZED)));
1364         setThrows(delegator, cp, meth);
1365         Bytecode code = new Bytecode(cp, 0, 0);
1366         code.addAload(0);
1367         int s = addLoadParameters(code, meth.getParameterTypes(), 1);
1368         Class<?> targetClass = invokespecialTarget(declClass);
1369         code.addInvokespecial(targetClass.isInterface(), cp.addClassInfo(targetClass.getName()),
1370                               meth.getName(), desc);
1371         addReturn(code, meth.getReturnType());
1372         code.setMaxLocals(++s);
1373         delegator.setCodeAttribute(code.toCodeAttribute());
1374         return delegator;
1375     }
1376 
1377     /* Suppose that the receiver type is S, the invoked method
1378      * is declared in T, and U is the immediate super class of S
1379      * (or its interface).  If S <: U <: T (S <: T reads "S extends T"),
1380      * the target type of invokespecial has to be not T but U.
1381      */
invokespecialTarget(Class<?> declClass)1382     private Class<?> invokespecialTarget(Class<?> declClass) {
1383         if (declClass.isInterface())
1384             for (Class<?> i: interfaces)
1385                 if (declClass.isAssignableFrom(i))
1386                     return i;
1387 
1388         return superClass;
1389     }
1390 
1391     /**
1392      * @param delegatorName     null if the original method is abstract.
1393      */
makeForwarder(String thisClassName, Method meth, String desc, ConstPool cp, Class<?> declClass, String delegatorName, int index, List<Find2MethodsArgs> forwarders)1394     private static MethodInfo makeForwarder(String thisClassName,
1395                     Method meth, String desc, ConstPool cp,
1396                     Class<?> declClass, String delegatorName, int index,
1397                     List<Find2MethodsArgs> forwarders) {
1398         MethodInfo forwarder = new MethodInfo(cp, meth.getName(), desc);
1399         forwarder.setAccessFlags(Modifier.FINAL
1400                     | (meth.getModifiers() & ~(Modifier.ABSTRACT
1401                                                | Modifier.NATIVE
1402                                                | Modifier.SYNCHRONIZED)));
1403         setThrows(forwarder, cp, meth);
1404         int args = Descriptor.paramSize(desc);
1405         Bytecode code = new Bytecode(cp, 0, args + 2);
1406         /*
1407          * static {
1408          *   methods[index * 2]
1409          *     = RuntimeSupport.findSuperMethod(this, <overridden name>, <desc>);
1410          *   methods[index * 2 + 1]
1411          *     = RuntimeSupport.findMethod(this, <delegator name>, <desc>);
1412          *     or = null // the original method is abstract.
1413          * }
1414          *     :
1415          * return ($r)handler.invoke(this, methods[index * 2],
1416          *                methods[index * 2 + 1], $args);
1417          */
1418         int origIndex = index * 2;
1419         int delIndex = index * 2 + 1;
1420         int arrayVar = args + 1;
1421         code.addGetstatic(thisClassName, HOLDER, HOLDER_TYPE);
1422         code.addAstore(arrayVar);
1423 
1424         forwarders.add(new Find2MethodsArgs(meth.getName(), delegatorName, desc, origIndex));
1425 
1426         code.addAload(0);
1427         code.addGetfield(thisClassName, HANDLER, HANDLER_TYPE);
1428         code.addAload(0);
1429 
1430         code.addAload(arrayVar);
1431         code.addIconst(origIndex);
1432         code.addOpcode(Opcode.AALOAD);
1433 
1434         code.addAload(arrayVar);
1435         code.addIconst(delIndex);
1436         code.addOpcode(Opcode.AALOAD);
1437 
1438         makeParameterList(code, meth.getParameterTypes());
1439         code.addInvokeinterface(MethodHandler.class.getName(), "invoke",
1440             "(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;",
1441             5);
1442         Class<?> retType = meth.getReturnType();
1443         addUnwrapper(code, retType);
1444         addReturn(code, retType);
1445 
1446         CodeAttribute ca = code.toCodeAttribute();
1447         forwarder.setCodeAttribute(ca);
1448         return forwarder;
1449     }
1450 
1451     static class Find2MethodsArgs {
1452         String methodName, delegatorName, descriptor;
1453         int origIndex;
1454 
Find2MethodsArgs(String mname, String dname, String desc, int index)1455         Find2MethodsArgs(String mname, String dname, String desc, int index) {
1456             methodName = mname;
1457             delegatorName = dname;
1458             descriptor = desc;
1459             origIndex = index;
1460         }
1461     }
1462 
setThrows(MethodInfo minfo, ConstPool cp, Method orig)1463     private static void setThrows(MethodInfo minfo, ConstPool cp, Method orig) {
1464         Class<?>[] exceptions = orig.getExceptionTypes();
1465         setThrows(minfo, cp, exceptions);
1466     }
1467 
setThrows(MethodInfo minfo, ConstPool cp, Class<?>[] exceptions)1468     private static void setThrows(MethodInfo minfo, ConstPool cp,
1469                                   Class<?>[] exceptions) {
1470         if (exceptions.length == 0)
1471             return;
1472 
1473         String[] list = new String[exceptions.length];
1474         for (int i = 0; i < exceptions.length; i++)
1475             list[i] = exceptions[i].getName();
1476 
1477         ExceptionsAttribute ea = new ExceptionsAttribute(cp);
1478         ea.setExceptions(list);
1479         minfo.setExceptionsAttribute(ea);
1480     }
1481 
addLoadParameters(Bytecode code, Class<?>[] params, int offset)1482     private static int addLoadParameters(Bytecode code, Class<?>[] params,
1483                                          int offset) {
1484         int stacksize = 0;
1485         int n = params.length;
1486         for (int i = 0; i < n; ++i)
1487             stacksize += addLoad(code, stacksize + offset, params[i]);
1488 
1489         return stacksize;
1490     }
1491 
addLoad(Bytecode code, int n, Class<?> type)1492     private static int addLoad(Bytecode code, int n, Class<?> type) {
1493         if (type.isPrimitive()) {
1494             if (type == Long.TYPE) {
1495                 code.addLload(n);
1496                 return 2;
1497             }
1498             else if (type == Float.TYPE)
1499                 code.addFload(n);
1500             else if (type == Double.TYPE) {
1501                 code.addDload(n);
1502                 return 2;
1503             }
1504             else
1505                 code.addIload(n);
1506         }
1507         else
1508             code.addAload(n);
1509 
1510         return 1;
1511     }
1512 
addReturn(Bytecode code, Class<?> type)1513     private static int addReturn(Bytecode code, Class<?> type) {
1514         if (type.isPrimitive()) {
1515             if (type == Long.TYPE) {
1516                 code.addOpcode(Opcode.LRETURN);
1517                 return 2;
1518             }
1519             else if (type == Float.TYPE)
1520                 code.addOpcode(Opcode.FRETURN);
1521             else if (type == Double.TYPE) {
1522                 code.addOpcode(Opcode.DRETURN);
1523                 return 2;
1524             }
1525             else if (type == Void.TYPE) {
1526                 code.addOpcode(Opcode.RETURN);
1527                 return 0;
1528             }
1529             else
1530                 code.addOpcode(Opcode.IRETURN);
1531         }
1532         else
1533             code.addOpcode(Opcode.ARETURN);
1534 
1535         return 1;
1536     }
1537 
makeParameterList(Bytecode code, Class<?>[] params)1538     private static void makeParameterList(Bytecode code, Class<?>[] params) {
1539         int regno = 1;
1540         int n = params.length;
1541         code.addIconst(n);
1542         code.addAnewarray("java/lang/Object");
1543         for (int i = 0; i < n; i++) {
1544             code.addOpcode(Opcode.DUP);
1545             code.addIconst(i);
1546             Class<?> type = params[i];
1547             if (type.isPrimitive())
1548                 regno = makeWrapper(code, type, regno);
1549             else {
1550                 code.addAload(regno);
1551                 regno++;
1552             }
1553 
1554             code.addOpcode(Opcode.AASTORE);
1555         }
1556     }
1557 
makeWrapper(Bytecode code, Class<?> type, int regno)1558     private static int makeWrapper(Bytecode code, Class<?> type, int regno) {
1559         int index = FactoryHelper.typeIndex(type);
1560         String wrapper = FactoryHelper.wrapperTypes[index];
1561         code.addNew(wrapper);
1562         code.addOpcode(Opcode.DUP);
1563         addLoad(code, regno, type);
1564         code.addInvokespecial(wrapper, "<init>",
1565                               FactoryHelper.wrapperDesc[index]);
1566         return regno + FactoryHelper.dataSize[index];
1567     }
1568 
addUnwrapper(Bytecode code, Class<?> type)1569     private static void addUnwrapper(Bytecode code, Class<?> type) {
1570         if (type.isPrimitive()) {
1571             if (type == Void.TYPE)
1572                 code.addOpcode(Opcode.POP);
1573             else {
1574                 int index = FactoryHelper.typeIndex(type);
1575                 String wrapper = FactoryHelper.wrapperTypes[index];
1576                 code.addCheckcast(wrapper);
1577                 code.addInvokevirtual(wrapper,
1578                                       FactoryHelper.unwarpMethods[index],
1579                                       FactoryHelper.unwrapDesc[index]);
1580             }
1581         }
1582         else
1583             code.addCheckcast(type.getName());
1584     }
1585 
makeWriteReplace(ConstPool cp)1586     private static MethodInfo makeWriteReplace(ConstPool cp) {
1587         MethodInfo minfo = new MethodInfo(cp, "writeReplace", "()Ljava/lang/Object;");
1588         String[] list = new String[1];
1589         list[0] = "java.io.ObjectStreamException";
1590         ExceptionsAttribute ea = new ExceptionsAttribute(cp);
1591         ea.setExceptions(list);
1592         minfo.setExceptionsAttribute(ea);
1593         Bytecode code = new Bytecode(cp, 0, 1);
1594         code.addAload(0);
1595         code.addInvokestatic("javassist.util.proxy.RuntimeSupport",
1596                              "makeSerializedProxy",
1597                              "(Ljava/lang/Object;)Ljavassist/util/proxy/SerializedProxy;");
1598         code.addOpcode(Opcode.ARETURN);
1599         minfo.setCodeAttribute(code.toCodeAttribute());
1600         return minfo;
1601     }
1602 }
1603