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