• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
16 package org.mockito.cglib.proxy;
17 
18 import java.lang.reflect.InvocationTargetException;
19 import java.lang.reflect.Method;
20 import java.util.*;
21 
22 import org.mockito.asm.ClassVisitor;
23 import org.mockito.asm.Label;
24 import org.mockito.asm.Type;
25 import 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  */
59 public 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 {
newInstance(String type, String[] interfaces, CallbackFilter filter, Type[] callbackTypes, boolean useFactory, boolean interceptDuringConstruction, Long serialVersionUID)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      */
Enhancer()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      */
setSuperclass(Class superclass)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      */
setInterfaces(Class[] interfaces)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      */
setCallbackFilter(CallbackFilter filter)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      */
setCallback(final Callback callback)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      */
setCallbacks(Callback[] callbacks)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      */
setUseFactory(boolean useFactory)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      */
setInterceptDuringConstruction(boolean interceptDuringConstruction)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      */
setCallbackType(Class callbackType)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      */
setCallbackTypes(Class[] callbackTypes)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      */
create()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      */
create(Class[] argumentTypes, Object[] arguments)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      */
createClass()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      */
setSerialVersionUID(Long sUID)323     public void setSerialVersionUID(Long sUID) {
324         serialVersionUID = sUID;
325     }
326 
validate()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 
createHelper()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 
getDefaultClassLoader()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 
rename(Signature sig, int index)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      */
getMethods(Class superclass, Class[] interfaces, List methods)413     public static void getMethods(Class superclass, Class[] interfaces, List methods)
414     {
415         getMethods(superclass, interfaces, methods, null, null);
416     }
417 
getMethods(Class superclass, Class[] interfaces, List methods, List interfaceMethods, Set forcePublic)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 
generateClass(ClassVisitor v)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      */
filterConstructors(Class sc, List constructors)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 
firstInstance(Class type)533     protected Object firstInstance(Class type) throws Exception {
534         if (classOnly) {
535             return type;
536         } else {
537             return createUsingReflection(type);
538         }
539     }
540 
nextInstance(Object instance)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      */
registerCallbacks(Class generatedClass, Callback[] callbacks)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      */
registerStaticCallbacks(Class generatedClass, Callback[] callbacks)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      */
isEnhanced(Class type)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 
setThreadCallbacks(Class type, Callback[] callbacks)610     private static void setThreadCallbacks(Class type, Callback[] callbacks) {
611         setCallbacksHelper(type, callbacks, SET_THREAD_CALLBACKS_NAME);
612     }
613 
setCallbacksHelper(Class type, Callback[] callbacks, String methodName)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 
getCallbacksSetter(Class type, String methodName)628     private static Method getCallbacksSetter(Class type, String methodName) throws NoSuchMethodException {
629         return type.getDeclaredMethod(methodName, new Class[]{ Callback[].class });
630     }
631 
createUsingReflection(Class type)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      */
create(Class type, Callback callback)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      */
create(Class superclass, Class interfaces[], Callback callback)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      */
create(Class superclass, Class[] interfaces, CallbackFilter filter, Callback[] callbacks)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 
emitConstructors(ClassEmitter ce, List constructors)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 
getCallbackKeys()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 
emitGetCallback(ClassEmitter ce, int[] keys)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 
emitSetCallback(ClassEmitter ce, int[] keys)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 
emitSetCallbacks(ClassEmitter ce)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 
emitGetCallbacks(ClassEmitter ce)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 
emitNewInstanceCallbacks(ClassEmitter ce)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 
emitCommonNewInstance(CodeEmitter e)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 
emitNewInstanceCallback(ClassEmitter ce)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 
emitNewInstanceMultiarg(ClassEmitter ce, List constructors)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 
emitMethods(final ClassEmitter ce, List methods, List actualMethods)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 
emitSetThreadCallbacks(ClassEmitter ce)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 
emitSetStaticCallbacks(ClassEmitter ce)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 
emitCurrentCallback(CodeEmitter e, int index)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 
emitBindCallbacks(ClassEmitter ce)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 
getCallbackField(int index)1043     private static String getCallbackField(int index) {
1044         return "CGLIB$CALLBACK_" + index;
1045     }
1046 }
1047