1/*
2 * Copyright 2002,2003,2004 The Apache Software Foundation
3 *
4 *  Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *  Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.mockito.cglib.proxy;
17
18import java.lang.reflect.InvocationTargetException;
19import java.lang.reflect.Method;
20import java.util.*;
21
22import org.mockito.asm.ClassVisitor;
23import org.mockito.asm.Label;
24import org.mockito.asm.Type;
25import org.mockito.cglib.core.*;
26
27/**
28 * Generates dynamic subclasses to enable method interception. This
29 * class started as a substitute for the standard Dynamic Proxy support
30 * included with JDK 1.3, but one that allowed the proxies to extend a
31 * concrete base class, in addition to implementing interfaces. The dynamically
32 * generated subclasses override the non-final methods of the superclass and
33 * have hooks which callback to user-defined interceptor
34 * implementations.
35 * <p>
36 * The original and most general callback type is the {@link MethodInterceptor}, which
37 * in AOP terms enables "around advice"--that is, you can invoke custom code both before
38 * and after the invocation of the "super" method. In addition you can modify the
39 * arguments before calling the super method, or not call it at all.
40 * <p>
41 * Although <code>MethodInterceptor</code> is generic enough to meet any
42 * interception need, it is often overkill. For simplicity and performance, additional
43 * specialized callback types, such as {@link LazyLoader} are also available.
44 * Often a single callback will be used per enhanced class, but you can control
45 * which callback is used on a per-method basis with a {@link CallbackFilter}.
46 * <p>
47 * The most common uses of this class are embodied in the static helper methods. For
48 * advanced needs, such as customizing the <code>ClassLoader</code> to use, you should create
49 * a new instance of <code>Enhancer</code>. Other classes within CGLIB follow a similar pattern.
50 * <p>
51 * All enhanced objects implement the {@link Factory} interface, unless {@link #setUseFactory} is
52 * used to explicitly disable this feature. The <code>Factory</code> interface provides an API
53 * to change the callbacks of an existing object, as well as a faster and easier way to create
54 * new instances of the same type.
55 * <p>
56 * For an almost drop-in replacement for
57 * <code>java.lang.reflect.Proxy</code>, see the {@link Proxy} class.
58 */
59public class Enhancer extends AbstractClassGenerator
60{
61    private static final CallbackFilter ALL_ZERO = new CallbackFilter(){
62        public int accept(Method method, List<Method> allMethods) {
63            return 0;
64        }
65    };
66
67    private static final Source SOURCE = new Source(Enhancer.class.getName());
68    private static final EnhancerKey KEY_FACTORY =
69      (EnhancerKey)KeyFactory.create(EnhancerKey.class);
70
71    private static final String BOUND_FIELD = "CGLIB$BOUND";
72    private static final String THREAD_CALLBACKS_FIELD = "CGLIB$THREAD_CALLBACKS";
73    private static final String STATIC_CALLBACKS_FIELD = "CGLIB$STATIC_CALLBACKS";
74    private static final String SET_THREAD_CALLBACKS_NAME = "CGLIB$SET_THREAD_CALLBACKS";
75    private static final String SET_STATIC_CALLBACKS_NAME = "CGLIB$SET_STATIC_CALLBACKS";
76    private static final String CONSTRUCTED_FIELD = "CGLIB$CONSTRUCTED";
77
78    private static final Type FACTORY =
79      TypeUtils.parseType("org.mockito.cglib.proxy.Factory");
80    private static final Type ILLEGAL_STATE_EXCEPTION =
81      TypeUtils.parseType("IllegalStateException");
82    private static final Type ILLEGAL_ARGUMENT_EXCEPTION =
83      TypeUtils.parseType("IllegalArgumentException");
84    private static final Type THREAD_LOCAL =
85      TypeUtils.parseType("ThreadLocal");
86    private static final Type CALLBACK =
87      TypeUtils.parseType("org.mockito.cglib.proxy.Callback");
88    private static final Type CALLBACK_ARRAY =
89      Type.getType(Callback[].class);
90    private static final Signature CSTRUCT_NULL =
91      TypeUtils.parseConstructor("");
92    private static final Signature SET_THREAD_CALLBACKS =
93      new Signature(SET_THREAD_CALLBACKS_NAME, Type.VOID_TYPE, new Type[]{ CALLBACK_ARRAY });
94    private static final Signature SET_STATIC_CALLBACKS =
95      new Signature(SET_STATIC_CALLBACKS_NAME, Type.VOID_TYPE, new Type[]{ CALLBACK_ARRAY });
96    private static final Signature NEW_INSTANCE =
97      new Signature("newInstance", Constants.TYPE_OBJECT, new Type[]{ CALLBACK_ARRAY });
98    private static final Signature MULTIARG_NEW_INSTANCE =
99      new Signature("newInstance", Constants.TYPE_OBJECT, new Type[]{
100          Constants.TYPE_CLASS_ARRAY,
101          Constants.TYPE_OBJECT_ARRAY,
102          CALLBACK_ARRAY,
103      });
104    private static final Signature SINGLE_NEW_INSTANCE =
105      new Signature("newInstance", Constants.TYPE_OBJECT, new Type[]{ CALLBACK });
106    private static final Signature SET_CALLBACK =
107      new Signature("setCallback", Type.VOID_TYPE, new Type[]{ Type.INT_TYPE, CALLBACK });
108    private static final Signature GET_CALLBACK =
109      new Signature("getCallback", CALLBACK, new Type[]{ Type.INT_TYPE });
110    private static final Signature SET_CALLBACKS =
111      new Signature("setCallbacks", Type.VOID_TYPE, new Type[]{ CALLBACK_ARRAY });
112    private static final Signature GET_CALLBACKS =
113      new Signature("getCallbacks", CALLBACK_ARRAY, new Type[0]);
114    private static final Signature THREAD_LOCAL_GET =
115      TypeUtils.parseSignature("Object get()");
116    private static final Signature THREAD_LOCAL_SET =
117      TypeUtils.parseSignature("void set(Object)");
118    private static final Signature BIND_CALLBACKS =
119      TypeUtils.parseSignature("void CGLIB$BIND_CALLBACKS(Object)");
120
121    /** Internal interface, only public due to ClassLoader issues. */
122    public interface EnhancerKey {
123        public Object newInstance(String type,
124                                  String[] interfaces,
125                                  CallbackFilter filter,
126                                  Type[] callbackTypes,
127                                  boolean useFactory,
128                                  boolean interceptDuringConstruction,
129                                  Long serialVersionUID);
130    }
131
132    private Class[] interfaces;
133    private CallbackFilter filter;
134    private Callback[] callbacks;
135    private Type[] callbackTypes;
136    private boolean classOnly;
137    private Class superclass;
138    private Class[] argumentTypes;
139    private Object[] arguments;
140    private boolean useFactory = true;
141    private Long serialVersionUID;
142    private boolean interceptDuringConstruction = true;
143
144    /**
145     * Create a new <code>Enhancer</code>. A new <code>Enhancer</code>
146     * object should be used for each generated object, and should not
147     * be shared across threads. To create additional instances of a
148     * generated class, use the <code>Factory</code> interface.
149     * @see Factory
150     */
151    public Enhancer() {
152        super(SOURCE);
153    }
154
155    /**
156     * Set the class which the generated class will extend. As a convenience,
157     * if the supplied superclass is actually an interface, <code>setInterfaces</code>
158     * will be called with the appropriate argument instead.
159     * A non-interface argument must not be declared as final, and must have an
160     * accessible constructor.
161     * @param superclass class to extend or interface to implement
162     * @see #setInterfaces(Class[])
163     */
164    public void setSuperclass(Class superclass) {
165        if (superclass != null && superclass.isInterface()) {
166            setInterfaces(new Class[]{ superclass });
167        } else if (superclass != null && superclass.equals(Object.class)) {
168            // affects choice of ClassLoader
169            this.superclass = null;
170        } else {
171            this.superclass = superclass;
172        }
173    }
174
175    /**
176     * Set the interfaces to implement. The <code>Factory</code> interface will
177     * always be implemented regardless of what is specified here.
178     * @param interfaces array of interfaces to implement, or null
179     * @see Factory
180     */
181    public void setInterfaces(Class[] interfaces) {
182        this.interfaces = interfaces;
183    }
184
185    /**
186     * Set the {@link CallbackFilter} used to map the generated class' methods
187     * to a particular callback index.
188     * New object instances will always use the same mapping, but may use different
189     * actual callback objects.
190     * @param filter the callback filter to use when generating a new class
191     * @see #setCallbacks
192     */
193    public void setCallbackFilter(CallbackFilter filter) {
194        this.filter = filter;
195    }
196
197
198    /**
199     * Set the single {@link Callback} to use.
200     * Ignored if you use {@link #createClass}.
201     * @param callback the callback to use for all methods
202     * @see #setCallbacks
203     */
204    public void setCallback(final Callback callback) {
205        setCallbacks(new Callback[]{ callback });
206    }
207
208    /**
209     * Set the array of callbacks to use.
210     * Ignored if you use {@link #createClass}.
211     * You must use a {@link CallbackFilter} to specify the index into this
212     * array for each method in the proxied class.
213     * @param callbacks the callback array
214     * @see #setCallbackFilter
215     * @see #setCallback
216     */
217    public void setCallbacks(Callback[] callbacks) {
218        if (callbacks != null && callbacks.length == 0) {
219            throw new IllegalArgumentException("Array cannot be empty");
220        }
221        this.callbacks = callbacks;
222    }
223
224    /**
225     * Set whether the enhanced object instances should implement
226     * the {@link Factory} interface.
227     * This was added for tools that need for proxies to be more
228     * indistinguishable from their targets. Also, in some cases it may
229     * be necessary to disable the <code>Factory</code> interface to
230     * prevent code from changing the underlying callbacks.
231     * @param useFactory whether to implement <code>Factory</code>; default is <code>true</code>
232     */
233    public void setUseFactory(boolean useFactory) {
234        this.useFactory = useFactory;
235    }
236
237    /**
238     * Set whether methods called from within the proxy's constructer
239     * will be intercepted. The default value is true. Unintercepted methods
240     * will call the method of the proxy's base class, if it exists.
241     * @param interceptDuringConstruction whether to intercept methods called from the constructor
242     */
243    public void setInterceptDuringConstruction(boolean interceptDuringConstruction) {
244        this.interceptDuringConstruction = interceptDuringConstruction;
245    }
246
247    /**
248     * Set the single type of {@link Callback} to use.
249     * This may be used instead of {@link #setCallback} when calling
250     * {@link #createClass}, since it may not be possible to have
251     * an array of actual callback instances.
252     * @param callbackType the type of callback to use for all methods
253     * @see #setCallbackTypes
254     */
255    public void setCallbackType(Class callbackType) {
256        setCallbackTypes(new Class[]{ callbackType });
257    }
258
259    /**
260     * Set the array of callback types to use.
261     * This may be used instead of {@link #setCallbacks} when calling
262     * {@link #createClass}, since it may not be possible to have
263     * an array of actual callback instances.
264     * You must use a {@link CallbackFilter} to specify the index into this
265     * array for each method in the proxied class.
266     * @param callbackTypes the array of callback types
267     */
268    public void setCallbackTypes(Class[] callbackTypes) {
269        if (callbackTypes != null && callbackTypes.length == 0) {
270            throw new IllegalArgumentException("Array cannot be empty");
271        }
272        this.callbackTypes = CallbackInfo.determineTypes(callbackTypes);
273    }
274
275    /**
276     * Generate a new class if necessary and uses the specified
277     * callbacks (if any) to create a new object instance.
278     * Uses the no-arg constructor of the superclass.
279     * @return a new instance
280     */
281    public Object create() {
282        classOnly = false;
283        argumentTypes = null;
284        return createHelper();
285    }
286
287    /**
288     * Generate a new class if necessary and uses the specified
289     * callbacks (if any) to create a new object instance.
290     * Uses the constructor of the superclass matching the <code>argumentTypes</code>
291     * parameter, with the given arguments.
292     * @param argumentTypes constructor signature
293     * @param arguments compatible wrapped arguments to pass to constructor
294     * @return a new instance
295     */
296    public Object create(Class[] argumentTypes, Object[] arguments) {
297        classOnly = false;
298        if (argumentTypes == null || arguments == null || argumentTypes.length != arguments.length) {
299            throw new IllegalArgumentException("Arguments must be non-null and of equal length");
300        }
301        this.argumentTypes = argumentTypes;
302        this.arguments = arguments;
303        return createHelper();
304    }
305
306    /**
307     * Generate a new class if necessary and return it without creating a new instance.
308     * This ignores any callbacks that have been set.
309     * To create a new instance you will have to use reflection, and methods
310     * called during the constructor will not be intercepted. To avoid this problem,
311     * use the multi-arg <code>create</code> method.
312     * @see #create(Class[], Object[])
313     */
314    public Class createClass() {
315        classOnly = true;
316        return (Class)createHelper();
317    }
318
319    /**
320     * Insert a static serialVersionUID field into the generated class.
321     * @param sUID the field value, or null to avoid generating field.
322     */
323    public void setSerialVersionUID(Long sUID) {
324        serialVersionUID = sUID;
325    }
326
327    private void validate() {
328        if (classOnly ^ (callbacks == null)) {
329            if (classOnly) {
330                throw new IllegalStateException("createClass does not accept callbacks");
331            } else {
332                throw new IllegalStateException("Callbacks are required");
333            }
334        }
335        if (classOnly && (callbackTypes == null)) {
336            throw new IllegalStateException("Callback types are required");
337        }
338        if (callbacks != null && callbackTypes != null) {
339            if (callbacks.length != callbackTypes.length) {
340                throw new IllegalStateException("Lengths of callback and callback types array must be the same");
341            }
342            Type[] check = CallbackInfo.determineTypes(callbacks);
343            for (int i = 0; i < check.length; i++) {
344                if (!check[i].equals(callbackTypes[i])) {
345                    throw new IllegalStateException("Callback " + check[i] + " is not assignable to " + callbackTypes[i]);
346                }
347            }
348        } else if (callbacks != null) {
349            callbackTypes = CallbackInfo.determineTypes(callbacks);
350        }
351        if (filter == null) {
352            if (callbackTypes.length > 1) {
353                throw new IllegalStateException("Multiple callback types possible but no filter specified");
354            }
355            filter = ALL_ZERO;
356        }
357        if (interfaces != null) {
358            for (int i = 0; i < interfaces.length; i++) {
359                if (interfaces[i] == null) {
360                    throw new IllegalStateException("Interfaces cannot be null");
361                }
362                if (!interfaces[i].isInterface()) {
363                    throw new IllegalStateException(interfaces[i] + " is not an interface");
364                }
365            }
366        }
367    }
368
369    private Object createHelper() {
370        validate();
371        if (superclass != null) {
372            setNamePrefix(superclass.getName());
373        } else if (interfaces != null) {
374            setNamePrefix(interfaces[ReflectUtils.findPackageProtected(interfaces)].getName());
375        }
376        return super.create(KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
377                                                    ReflectUtils.getNames(interfaces),
378                                                    filter,
379                                                    callbackTypes,
380                                                    useFactory,
381                                                    interceptDuringConstruction,
382                                                    serialVersionUID));
383    }
384
385    protected ClassLoader getDefaultClassLoader() {
386        if (superclass != null) {
387            return superclass.getClassLoader();
388        } else if (interfaces != null) {
389            return interfaces[0].getClassLoader();
390        } else {
391            return null;
392        }
393    }
394
395    private Signature rename(Signature sig, int index) {
396        return new Signature("CGLIB$" + sig.getName() + "$" + index,
397                             sig.getDescriptor());
398    }
399
400    /**
401     * Finds all of the methods that will be extended by an
402     * Enhancer-generated class using the specified superclass and
403     * interfaces. This can be useful in building a list of Callback
404     * objects. The methods are added to the end of the given list.  Due
405     * to the subclassing nature of the classes generated by Enhancer,
406     * the methods are guaranteed to be non-static, non-final, and
407     * non-private. Each method signature will only occur once, even if
408     * it occurs in multiple classes.
409     * @param superclass the class that will be extended, or null
410     * @param interfaces the list of interfaces that will be implemented, or null
411     * @param methods the list into which to copy the applicable methods
412     */
413    public static void getMethods(Class superclass, Class[] interfaces, List methods)
414    {
415        getMethods(superclass, interfaces, methods, null, null);
416    }
417
418    private static void getMethods(Class superclass, Class[] interfaces, List methods, List interfaceMethods, Set forcePublic)
419    {
420        ReflectUtils.addAllMethods(superclass, methods);
421        List target = (interfaceMethods != null) ? interfaceMethods : methods;
422        if (interfaces != null) {
423            for (int i = 0; i < interfaces.length; i++) {
424                if (interfaces[i] != Factory.class) {
425                    ReflectUtils.addAllMethods(interfaces[i], target);
426                }
427            }
428        }
429        if (interfaceMethods != null) {
430            if (forcePublic != null) {
431                forcePublic.addAll(MethodWrapper.createSet(interfaceMethods));
432            }
433            methods.addAll(interfaceMethods);
434        }
435        CollectionUtils.filter(methods, new RejectModifierPredicate(Constants.ACC_STATIC));
436        CollectionUtils.filter(methods, new VisibilityPredicate(superclass, true));
437        CollectionUtils.filter(methods, new DuplicatesPredicate());
438        CollectionUtils.filter(methods, new RejectModifierPredicate(Constants.ACC_FINAL));
439    }
440
441    public void generateClass(ClassVisitor v) throws Exception {
442        Class sc = (superclass == null) ? Object.class : superclass;
443
444        if (TypeUtils.isFinal(sc.getModifiers()))
445            throw new IllegalArgumentException("Cannot subclass final class " + sc);
446        List constructors = new ArrayList(Arrays.asList(sc.getDeclaredConstructors()));
447        filterConstructors(sc, constructors);
448
449        // Order is very important: must add superclass, then
450        // its superclass chain, then each interface and
451        // its superinterfaces.
452        List actualMethods = new ArrayList();
453        List interfaceMethods = new ArrayList();
454        final Set forcePublic = new HashSet();
455        getMethods(sc, interfaces, actualMethods, interfaceMethods, forcePublic);
456
457        List methods = CollectionUtils.transform(actualMethods, new Transformer() {
458            public Object transform(Object value) {
459                Method method = (Method)value;
460                int modifiers = Constants.ACC_FINAL
461                    | (method.getModifiers()
462                       & ~Constants.ACC_ABSTRACT
463                       & ~Constants.ACC_NATIVE
464                       & ~Constants.ACC_SYNCHRONIZED);
465                if (forcePublic.contains(MethodWrapper.create(method))) {
466                    modifiers = (modifiers & ~Constants.ACC_PROTECTED) | Constants.ACC_PUBLIC;
467                }
468                return ReflectUtils.getMethodInfo(method, modifiers);
469            }
470        });
471
472        ClassEmitter e = new ClassEmitter(v);
473        e.begin_class(Constants.V1_2,
474                      Constants.ACC_PUBLIC,
475                      getClassName(),
476                      Type.getType(sc),
477                      (useFactory ?
478                       TypeUtils.add(TypeUtils.getTypes(interfaces), FACTORY) :
479                       TypeUtils.getTypes(interfaces)),
480                      Constants.SOURCE_FILE);
481        List constructorInfo = CollectionUtils.transform(constructors, MethodInfoTransformer.getInstance());
482
483        e.declare_field(Constants.ACC_PRIVATE, BOUND_FIELD, Type.BOOLEAN_TYPE, null);
484        if (!interceptDuringConstruction) {
485            e.declare_field(Constants.ACC_PRIVATE, CONSTRUCTED_FIELD, Type.BOOLEAN_TYPE, null);
486        }
487        e.declare_field(Constants.PRIVATE_FINAL_STATIC, THREAD_CALLBACKS_FIELD, THREAD_LOCAL, null);
488        e.declare_field(Constants.PRIVATE_FINAL_STATIC, STATIC_CALLBACKS_FIELD, CALLBACK_ARRAY, null);
489        if (serialVersionUID != null) {
490            e.declare_field(Constants.PRIVATE_FINAL_STATIC, Constants.SUID_FIELD_NAME, Type.LONG_TYPE, serialVersionUID);
491        }
492
493        for (int i = 0; i < callbackTypes.length; i++) {
494            e.declare_field(Constants.ACC_PRIVATE, getCallbackField(i), callbackTypes[i], null);
495        }
496
497        emitMethods(e, methods, actualMethods);
498        emitConstructors(e, constructorInfo);
499        emitSetThreadCallbacks(e);
500        emitSetStaticCallbacks(e);
501        emitBindCallbacks(e);
502
503        if (useFactory) {
504            int[] keys = getCallbackKeys();
505            emitNewInstanceCallbacks(e);
506            emitNewInstanceCallback(e);
507            emitNewInstanceMultiarg(e, constructorInfo);
508            emitGetCallback(e, keys);
509            emitSetCallback(e, keys);
510            emitGetCallbacks(e);
511            emitSetCallbacks(e);
512        }
513
514        e.end_class();
515    }
516
517    /**
518     * Filter the list of constructors from the superclass. The
519     * constructors which remain will be included in the generated
520     * class. The default implementation is to filter out all private
521     * constructors, but subclasses may extend Enhancer to override this
522     * behavior.
523     * @param sc the superclass
524     * @param constructors the list of all declared constructors from the superclass
525     * @throws IllegalArgumentException if there are no non-private constructors
526     */
527    protected void filterConstructors(Class sc, List constructors) {
528        CollectionUtils.filter(constructors, new VisibilityPredicate(sc, true));
529        if (constructors.size() == 0)
530            throw new IllegalArgumentException("No visible constructors in " + sc);
531    }
532
533    protected Object firstInstance(Class type) throws Exception {
534        if (classOnly) {
535            return type;
536        } else {
537            return createUsingReflection(type);
538        }
539    }
540
541    protected Object nextInstance(Object instance) {
542        Class protoclass = (instance instanceof Class) ? (Class)instance : instance.getClass();
543        if (classOnly) {
544            return protoclass;
545        } else if (instance instanceof Factory) {
546            if (argumentTypes != null) {
547                return ((Factory)instance).newInstance(argumentTypes, arguments, callbacks);
548            } else {
549                return ((Factory)instance).newInstance(callbacks);
550            }
551        } else {
552            return createUsingReflection(protoclass);
553        }
554    }
555
556    /**
557     * Call this method to register the {@link Callback} array to use before
558     * creating a new instance of the generated class via reflection. If you are using
559     * an instance of <code>Enhancer</code> or the {@link Factory} interface to create
560     * new instances, this method is unnecessary. Its primary use is for when you want to
561     * cache and reuse a generated class yourself, and the generated class does
562     * <i>not</i> implement the {@link Factory} interface.
563     * <p>
564     * Note that this method only registers the callbacks on the current thread.
565     * If you want to register callbacks for instances created by multiple threads,
566     * use {@link #registerStaticCallbacks}.
567     * <p>
568     * The registered callbacks are overwritten and subsequently cleared
569     * when calling any of the <code>create</code> methods (such as
570     * {@link #create}), or any {@link Factory} <code>newInstance</code> method.
571     * Otherwise they are <i>not</i> cleared, and you should be careful to set them
572     * back to <code>null</code> after creating new instances via reflection if
573     * memory leakage is a concern.
574     * @param generatedClass a class previously created by {@link Enhancer}
575     * @param callbacks the array of callbacks to use when instances of the generated
576     * class are created
577     * @see #setUseFactory
578     */
579    public static void registerCallbacks(Class generatedClass, Callback[] callbacks) {
580        setThreadCallbacks(generatedClass, callbacks);
581    }
582
583    /**
584     * Similar to {@link #registerCallbacks}, but suitable for use
585     * when multiple threads will be creating instances of the generated class.
586     * The thread-level callbacks will always override the static callbacks.
587     * Static callbacks are never cleared.
588     * @param generatedClass a class previously created by {@link Enhancer}
589     * @param callbacks the array of callbacks to use when instances of the generated
590     * class are created
591     */
592    public static void registerStaticCallbacks(Class generatedClass, Callback[] callbacks) {
593        setCallbacksHelper(generatedClass, callbacks, SET_STATIC_CALLBACKS_NAME);
594    }
595
596    /**
597     * Determine if a class was generated using <code>Enhancer</code>.
598     * @param type any class
599     * @return whether the class was generated  using <code>Enhancer</code>
600     */
601    public static boolean isEnhanced(Class type) {
602        try {
603            getCallbacksSetter(type, SET_THREAD_CALLBACKS_NAME);
604            return true;
605        } catch (NoSuchMethodException e) {
606            return false;
607        }
608    }
609
610    private static void setThreadCallbacks(Class type, Callback[] callbacks) {
611        setCallbacksHelper(type, callbacks, SET_THREAD_CALLBACKS_NAME);
612    }
613
614    private static void setCallbacksHelper(Class type, Callback[] callbacks, String methodName) {
615        // TODO: optimize
616        try {
617            Method setter = getCallbacksSetter(type, methodName);
618            setter.invoke(null, new Object[]{ callbacks });
619        } catch (NoSuchMethodException e) {
620            throw new IllegalArgumentException(type + " is not an enhanced class");
621        } catch (IllegalAccessException e) {
622            throw new CodeGenerationException(e);
623        } catch (InvocationTargetException e) {
624            throw new CodeGenerationException(e);
625        }
626    }
627
628    private static Method getCallbacksSetter(Class type, String methodName) throws NoSuchMethodException {
629        return type.getDeclaredMethod(methodName, new Class[]{ Callback[].class });
630    }
631
632    private Object createUsingReflection(Class type) {
633        setThreadCallbacks(type, callbacks);
634        try{
635
636        if (argumentTypes != null) {
637
638             return ReflectUtils.newInstance(type, argumentTypes, arguments);
639
640        } else {
641
642            return ReflectUtils.newInstance(type);
643
644        }
645        }finally{
646         // clear thread callbacks to allow them to be gc'd
647         setThreadCallbacks(type, null);
648        }
649    }
650
651    /**
652     * Helper method to create an intercepted object.
653     * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
654     * instead of this static method.
655     * @param type class to extend or interface to implement
656     * @param callback the callback to use for all methods
657     */
658    public static Object create(Class type, Callback callback) {
659        Enhancer e = new Enhancer();
660        e.setSuperclass(type);
661        e.setCallback(callback);
662        return e.create();
663    }
664
665    /**
666     * Helper method to create an intercepted object.
667     * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
668     * instead of this static method.
669     * @param type class to extend or interface to implement
670     * @param interfaces array of interfaces to implement, or null
671     * @param callback the callback to use for all methods
672     */
673    public static Object create(Class superclass, Class interfaces[], Callback callback) {
674        Enhancer e = new Enhancer();
675        e.setSuperclass(superclass);
676        e.setInterfaces(interfaces);
677        e.setCallback(callback);
678        return e.create();
679    }
680
681    /**
682     * Helper method to create an intercepted object.
683     * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
684     * instead of this static method.
685     * @param type class to extend or interface to implement
686     * @param interfaces array of interfaces to implement, or null
687     * @param filter the callback filter to use when generating a new class
688     * @param callbacks callback implementations to use for the enhanced object
689     */
690    public static Object create(Class superclass, Class[] interfaces, CallbackFilter filter, Callback[] callbacks) {
691        Enhancer e = new Enhancer();
692        e.setSuperclass(superclass);
693        e.setInterfaces(interfaces);
694        e.setCallbackFilter(filter);
695        e.setCallbacks(callbacks);
696        return e.create();
697    }
698
699    private void emitConstructors(ClassEmitter ce, List constructors) {
700        boolean seenNull = false;
701        for (Iterator it = constructors.iterator(); it.hasNext();) {
702            MethodInfo constructor = (MethodInfo)it.next();
703            CodeEmitter e = EmitUtils.begin_method(ce, constructor, Constants.ACC_PUBLIC);
704            e.load_this();
705            e.dup();
706            e.load_args();
707            Signature sig = constructor.getSignature();
708            seenNull = seenNull || sig.getDescriptor().equals("()V");
709            e.super_invoke_constructor(sig);
710            e.invoke_static_this(BIND_CALLBACKS);
711            if (!interceptDuringConstruction) {
712                e.load_this();
713                e.push(1);
714                e.putfield(CONSTRUCTED_FIELD);
715            }
716            e.return_value();
717            e.end_method();
718        }
719        if (!classOnly && !seenNull && arguments == null)
720            throw new IllegalArgumentException("Superclass has no null constructors but no arguments were given");
721    }
722
723    private int[] getCallbackKeys() {
724        int[] keys = new int[callbackTypes.length];
725        for (int i = 0; i < callbackTypes.length; i++) {
726            keys[i] = i;
727        }
728        return keys;
729    }
730
731    private void emitGetCallback(ClassEmitter ce, int[] keys) {
732        final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, GET_CALLBACK, null);
733        e.load_this();
734        e.invoke_static_this(BIND_CALLBACKS);
735        e.load_this();
736        e.load_arg(0);
737        e.process_switch(keys, new ProcessSwitchCallback() {
738            public void processCase(int key, Label end) {
739                e.getfield(getCallbackField(key));
740                e.goTo(end);
741            }
742            public void processDefault() {
743                e.pop(); // stack height
744                e.aconst_null();
745            }
746        });
747        e.return_value();
748        e.end_method();
749    }
750
751    private void emitSetCallback(ClassEmitter ce, int[] keys) {
752        final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, SET_CALLBACK, null);
753        e.load_arg(0);
754        e.process_switch(keys, new ProcessSwitchCallback() {
755            public void processCase(int key, Label end) {
756                e.load_this();
757                e.load_arg(1);
758                e.checkcast(callbackTypes[key]);
759                e.putfield(getCallbackField(key));
760                e.goTo(end);
761            }
762            public void processDefault() {
763                // TODO: error?
764            }
765        });
766        e.return_value();
767        e.end_method();
768    }
769
770    private void emitSetCallbacks(ClassEmitter ce) {
771        CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, SET_CALLBACKS, null);
772        e.load_this();
773        e.load_arg(0);
774        for (int i = 0; i < callbackTypes.length; i++) {
775            e.dup2();
776            e.aaload(i);
777            e.checkcast(callbackTypes[i]);
778            e.putfield(getCallbackField(i));
779        }
780        e.return_value();
781        e.end_method();
782    }
783
784    private void emitGetCallbacks(ClassEmitter ce) {
785        CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, GET_CALLBACKS, null);
786        e.load_this();
787        e.invoke_static_this(BIND_CALLBACKS);
788        e.load_this();
789        e.push(callbackTypes.length);
790        e.newarray(CALLBACK);
791        for (int i = 0; i < callbackTypes.length; i++) {
792            e.dup();
793            e.push(i);
794            e.load_this();
795            e.getfield(getCallbackField(i));
796            e.aastore();
797        }
798        e.return_value();
799        e.end_method();
800    }
801
802    private void emitNewInstanceCallbacks(ClassEmitter ce) {
803        CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null);
804        e.load_arg(0);
805        e.invoke_static_this(SET_THREAD_CALLBACKS);
806        emitCommonNewInstance(e);
807    }
808
809    private void emitCommonNewInstance(CodeEmitter e) {
810        e.new_instance_this();
811        e.dup();
812        e.invoke_constructor_this();
813        e.aconst_null();
814        e.invoke_static_this(SET_THREAD_CALLBACKS);
815        e.return_value();
816        e.end_method();
817    }
818
819    private void emitNewInstanceCallback(ClassEmitter ce) {
820        CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, SINGLE_NEW_INSTANCE, null);
821        switch (callbackTypes.length) {
822        case 0:
823            // TODO: make sure Callback is null
824            break;
825        case 1:
826            // for now just make a new array; TODO: optimize
827            e.push(1);
828            e.newarray(CALLBACK);
829            e.dup();
830            e.push(0);
831            e.load_arg(0);
832            e.aastore();
833            e.invoke_static_this(SET_THREAD_CALLBACKS);
834            break;
835        default:
836            e.throw_exception(ILLEGAL_STATE_EXCEPTION, "More than one callback object required");
837        }
838        emitCommonNewInstance(e);
839    }
840
841    private void emitNewInstanceMultiarg(ClassEmitter ce, List constructors) {
842        final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, MULTIARG_NEW_INSTANCE, null);
843        e.load_arg(2);
844        e.invoke_static_this(SET_THREAD_CALLBACKS);
845        e.new_instance_this();
846        e.dup();
847        e.load_arg(0);
848        EmitUtils.constructor_switch(e, constructors, new ObjectSwitchCallback() {
849            public void processCase(Object key, Label end) {
850                MethodInfo constructor = (MethodInfo)key;
851                Type types[] = constructor.getSignature().getArgumentTypes();
852                for (int i = 0; i < types.length; i++) {
853                    e.load_arg(1);
854                    e.push(i);
855                    e.aaload();
856                    e.unbox(types[i]);
857                }
858                e.invoke_constructor_this(constructor.getSignature());
859                e.goTo(end);
860            }
861            public void processDefault() {
862                e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Constructor not found");
863            }
864        });
865        e.aconst_null();
866        e.invoke_static_this(SET_THREAD_CALLBACKS);
867        e.return_value();
868        e.end_method();
869    }
870
871    private void emitMethods(final ClassEmitter ce, List methods, List actualMethods) {
872        CallbackGenerator[] generators = CallbackInfo.getGenerators(callbackTypes);
873
874        Map groups = new HashMap();
875        final Map indexes = new HashMap();
876        final Map originalModifiers = new HashMap();
877        final Map positions = CollectionUtils.getIndexMap(methods);
878
879        Iterator it1 = methods.iterator();
880        Iterator it2 = (actualMethods != null) ? actualMethods.iterator() : null;
881
882        while (it1.hasNext()) {
883            MethodInfo method = (MethodInfo)it1.next();
884            Method actualMethod = (it2 != null) ? (Method)it2.next() : null;
885            int index = filter.accept(actualMethod, actualMethods);
886            if (index >= callbackTypes.length) {
887                throw new IllegalArgumentException("Callback filter returned an index that is too large: " + index);
888            }
889            originalModifiers.put(method, new Integer((actualMethod != null) ? actualMethod.getModifiers() : method.getModifiers()));
890            indexes.put(method, new Integer(index));
891            List group = (List)groups.get(generators[index]);
892            if (group == null) {
893                groups.put(generators[index], group = new ArrayList(methods.size()));
894            }
895            group.add(method);
896        }
897
898        Set seenGen = new HashSet();
899        CodeEmitter se = ce.getStaticHook();
900        se.new_instance(THREAD_LOCAL);
901        se.dup();
902        se.invoke_constructor(THREAD_LOCAL, CSTRUCT_NULL);
903        se.putfield(THREAD_CALLBACKS_FIELD);
904
905        final Object[] state = new Object[1];
906        CallbackGenerator.Context context = new CallbackGenerator.Context() {
907            public ClassLoader getClassLoader() {
908                return Enhancer.this.getClassLoader();
909            }
910            public int getOriginalModifiers(MethodInfo method) {
911                return ((Integer)originalModifiers.get(method)).intValue();
912            }
913            public int getIndex(MethodInfo method) {
914                return ((Integer)indexes.get(method)).intValue();
915            }
916            public void emitCallback(CodeEmitter e, int index) {
917                emitCurrentCallback(e, index);
918            }
919            public Signature getImplSignature(MethodInfo method) {
920                return rename(method.getSignature(), ((Integer)positions.get(method)).intValue());
921            }
922            public CodeEmitter beginMethod(ClassEmitter ce, MethodInfo method) {
923                CodeEmitter e = EmitUtils.begin_method(ce, method);
924                if (!interceptDuringConstruction &&
925                    !TypeUtils.isAbstract(method.getModifiers())) {
926                    Label constructed = e.make_label();
927                    e.load_this();
928                    e.getfield(CONSTRUCTED_FIELD);
929                    e.if_jump(e.NE, constructed);
930                    e.load_this();
931                    e.load_args();
932                    e.super_invoke();
933                    e.return_value();
934                    e.mark(constructed);
935                }
936                return e;
937            }
938        };
939        for (int i = 0; i < callbackTypes.length; i++) {
940            CallbackGenerator gen = generators[i];
941            if (!seenGen.contains(gen)) {
942                seenGen.add(gen);
943                final List fmethods = (List)groups.get(gen);
944                if (fmethods != null) {
945                    try {
946                        gen.generate(ce, context, fmethods);
947                        gen.generateStatic(se, context, fmethods);
948                    } catch (RuntimeException x) {
949                        throw x;
950                    } catch (Exception x) {
951                        throw new CodeGenerationException(x);
952                    }
953                }
954            }
955        }
956        se.return_value();
957        se.end_method();
958    }
959
960    private void emitSetThreadCallbacks(ClassEmitter ce) {
961        CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC,
962                                        SET_THREAD_CALLBACKS,
963                                        null);
964        e.getfield(THREAD_CALLBACKS_FIELD);
965        e.load_arg(0);
966        e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_SET);
967        e.return_value();
968        e.end_method();
969    }
970
971    private void emitSetStaticCallbacks(ClassEmitter ce) {
972        CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC,
973                                        SET_STATIC_CALLBACKS,
974                                        null);
975        e.load_arg(0);
976        e.putfield(STATIC_CALLBACKS_FIELD);
977        e.return_value();
978        e.end_method();
979    }
980
981    private void emitCurrentCallback(CodeEmitter e, int index) {
982        e.load_this();
983        e.getfield(getCallbackField(index));
984        e.dup();
985        Label end = e.make_label();
986        e.ifnonnull(end);
987        e.pop(); // stack height
988        e.load_this();
989        e.invoke_static_this(BIND_CALLBACKS);
990        e.load_this();
991        e.getfield(getCallbackField(index));
992        e.mark(end);
993    }
994
995    private void emitBindCallbacks(ClassEmitter ce) {
996        CodeEmitter e = ce.begin_method(Constants.PRIVATE_FINAL_STATIC,
997                                        BIND_CALLBACKS,
998                                        null);
999        Local me = e.make_local();
1000        e.load_arg(0);
1001        e.checkcast_this();
1002        e.store_local(me);
1003
1004        Label end = e.make_label();
1005        e.load_local(me);
1006        e.getfield(BOUND_FIELD);
1007        e.if_jump(e.NE, end);
1008        e.load_local(me);
1009        e.push(1);
1010        e.putfield(BOUND_FIELD);
1011
1012        e.getfield(THREAD_CALLBACKS_FIELD);
1013        e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_GET);
1014        e.dup();
1015        Label found_callback = e.make_label();
1016        e.ifnonnull(found_callback);
1017        e.pop();
1018
1019        e.getfield(STATIC_CALLBACKS_FIELD);
1020        e.dup();
1021        e.ifnonnull(found_callback);
1022        e.pop();
1023        e.goTo(end);
1024
1025        e.mark(found_callback);
1026        e.checkcast(CALLBACK_ARRAY);
1027        e.load_local(me);
1028        e.swap();
1029        for (int i = callbackTypes.length - 1; i >= 0; i--) {
1030            if (i != 0) {
1031                e.dup2();
1032            }
1033            e.aaload(i);
1034            e.checkcast(callbackTypes[i]);
1035            e.putfield(getCallbackField(i));
1036        }
1037
1038        e.mark(end);
1039        e.return_value();
1040        e.end_method();
1041    }
1042
1043    private static String getCallbackField(int index) {
1044        return "CGLIB$CALLBACK_" + index;
1045    }
1046}
1047