2 * Copyright 2002,2003,2004 The Apache Software Foundation
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package net
.sf
.cglib
.proxy
;
18 import com
.intellij
.ide
.plugins
.PluginManager
;
19 import com
.intellij
.ide
.plugins
.cl
.PluginClassLoader
;
20 import com
.intellij
.util
.ReflectionCache
;
21 import org
.objectweb
.asm
.ClassVisitor
;
22 import org
.objectweb
.asm
.Label
;
23 import org
.objectweb
.asm
.Type
;
24 import net
.sf
.cglib
.core
.*;
26 import java
.lang
.reflect
.Constructor
;
27 import java
.lang
.reflect
.InvocationTargetException
;
28 import java
.lang
.reflect
.Method
;
32 * Generates dynamic subclasses to enable method interception. This
33 * class started as a substitute for the standard Dynamic Proxy support
34 * included with JDK 1.3, but one that allowed the proxies to extend a
35 * concrete base class, in addition to implementing interfaces. The dynamically
36 * generated subclasses override the non-final methods of the superclass and
37 * have hooks which callback to user-defined interceptor
40 * The original and most general callback type is the {@link MethodInterceptor}, which
41 * in AOP terms enables "around advice"--that is, you can invoke custom code both before
42 * and after the invocation of the "super" method. In addition you can modify the
43 * arguments before calling the super method, or not call it at all.
45 * Although <code>MethodInterceptor</code> is generic enough to meet any
46 * interception need, it is often overkill. For simplicity and performance, additional
47 * specialized callback types, such as {@link LazyLoader} are also available.
48 * Often a single callback will be used per enhanced class, but you can control
49 * which callback is used on a per-method basis with a {@link CallbackFilter}.
51 * The most common uses of this class are embodied in the static helper methods. For
52 * advanced needs, such as customizing the <code>ClassLoader</code> to use, you should create
53 * a new instance of <code>Enhancer</code>. Other classes within CGLIB follow a similar pattern.
55 * All enhanced objects implement the {@link Factory} interface, unless {@link #setUseFactory} is
56 * used to explicitly disable this feature. The <code>Factory</code> interface provides an API
57 * to change the callbacks of an existing object, as well as a faster and easier way to create
58 * new instances of the same type.
60 * For an almost drop-in replacement for
61 * <code>java.lang.reflect.Proxy</code>, see the {@link Proxy} class.
64 public class AdvancedEnhancer
extends AbstractClassGenerator
66 private static final CallbackFilter ALL_ZERO
= new CallbackFilter(){
67 public int accept(Method method
) {
72 private static final Source SOURCE
= new Source(Enhancer
.class.getName());
73 private static final EnhancerKey KEY_FACTORY
=
74 (EnhancerKey
)KeyFactory
.create(EnhancerKey
.class);
76 private static final String BOUND_FIELD
= "CGLIB$BOUND";
77 private static final String THREAD_CALLBACKS_FIELD
= "CGLIB$THREAD_CALLBACKS";
78 private static final String STATIC_CALLBACKS_FIELD
= "CGLIB$STATIC_CALLBACKS";
79 private static final String SET_THREAD_CALLBACKS_NAME
= "CGLIB$SET_THREAD_CALLBACKS";
80 private static final String SET_STATIC_CALLBACKS_NAME
= "CGLIB$SET_STATIC_CALLBACKS";
81 private static final String CONSTRUCTED_FIELD
= "CGLIB$CONSTRUCTED";
83 private static final Type FACTORY
=
84 TypeUtils
.parseType("net.sf.cglib.proxy.Factory");
85 private static final Type ILLEGAL_STATE_EXCEPTION
=
86 TypeUtils
.parseType("IllegalStateException");
87 private static final Type ILLEGAL_ARGUMENT_EXCEPTION
=
88 TypeUtils
.parseType("IllegalArgumentException");
89 private static final Type THREAD_LOCAL
=
90 TypeUtils
.parseType("ThreadLocal");
91 private static final Type CALLBACK
=
92 TypeUtils
.parseType("net.sf.cglib.proxy.Callback");
93 private static final Type CALLBACK_ARRAY
=
94 Type
.getType("[Lnet/sf/cglib/proxy/Callback;");
95 private static final Signature CSTRUCT_NULL
=
96 TypeUtils
.parseConstructor("");
97 private static final Signature SET_THREAD_CALLBACKS
=
98 new Signature(SET_THREAD_CALLBACKS_NAME
, Type
.VOID_TYPE
, new Type
[]{ CALLBACK_ARRAY
});
99 private static final Signature SET_STATIC_CALLBACKS
=
100 new Signature(SET_STATIC_CALLBACKS_NAME
, Type
.VOID_TYPE
, new Type
[]{ CALLBACK_ARRAY
});
101 private static final Signature NEW_INSTANCE
=
102 new Signature("newInstance", Constants
.TYPE_OBJECT
, new Type
[]{ CALLBACK_ARRAY
});
103 private static final Signature MULTIARG_NEW_INSTANCE
=
104 new Signature("newInstance", Constants
.TYPE_OBJECT
, new Type
[]{
105 Constants
.TYPE_CLASS_ARRAY
,
106 Constants
.TYPE_OBJECT_ARRAY
,
109 private static final Signature SINGLE_NEW_INSTANCE
=
110 new Signature("newInstance", Constants
.TYPE_OBJECT
, new Type
[]{ CALLBACK
});
111 private static final Signature SET_CALLBACK
=
112 new Signature("setCallback", Type
.VOID_TYPE
, new Type
[]{ Type
.INT_TYPE
, CALLBACK
});
113 private static final Signature GET_CALLBACK
=
114 new Signature("getCallback", CALLBACK
, new Type
[]{ Type
.INT_TYPE
});
115 private static final Signature SET_CALLBACKS
=
116 new Signature("setCallbacks", Type
.VOID_TYPE
, new Type
[]{ CALLBACK_ARRAY
});
117 private static final Signature GET_CALLBACKS
=
118 new Signature("getCallbacks", CALLBACK_ARRAY
, new Type
[0]);
119 private static final Signature THREAD_LOCAL_GET
=
120 TypeUtils
.parseSignature("Object get()");
121 private static final Signature THREAD_LOCAL_SET
=
122 TypeUtils
.parseSignature("void set(Object)");
123 private static final Signature BIND_CALLBACKS
=
124 TypeUtils
.parseSignature("void CGLIB$BIND_CALLBACKS(Object)");
126 /** Internal interface, only public due to ClassLoader issues. */
127 public interface EnhancerKey
{
128 public Object
newInstance(String type
,
130 CallbackFilter filter
,
131 Type
[] callbackTypes
,
133 boolean interceptDuringConstruction
,
134 Long serialVersionUID
);
137 private Class
[] interfaces
;
138 private CallbackFilter filter
;
139 private Callback
[] callbacks
;
140 private Type
[] callbackTypes
;
141 private boolean classOnly
;
142 private Class superclass
;
143 private Class
[] argumentTypes
;
144 private Object
[] arguments
;
145 private boolean useFactory
= true;
146 private Long serialVersionUID
;
147 private boolean interceptDuringConstruction
= true;
150 * Create a new <code>Enhancer</code>. A new <code>Enhancer</code>
151 * object should be used for each generated object, and should not
152 * be shared across threads. To create additional instances of a
153 * generated class, use the <code>Factory</code> interface.
156 public AdvancedEnhancer() {
161 * Set the class which the generated class will extend. As a convenience,
162 * if the supplied superclass is actually an interface, <code>setInterfaces</code>
163 * will be called with the appropriate argument instead.
164 * A non-interface argument must not be declared as final, and must have an
165 * accessible constructor.
166 * @param superclass class to extend or interface to implement
167 * @see #setInterfaces(Class[])
169 public void setSuperclass(Class superclass
) {
170 if (superclass
!= null && superclass
.isInterface()) {
171 setInterfaces(new Class
[]{ superclass
});
172 } else if (superclass
!= null && superclass
.equals(Object
.class)) {
173 // affects choice of ClassLoader
174 this.superclass
= null;
176 this.superclass
= superclass
;
181 * Set the interfaces to implement. The <code>Factory</code> interface will
182 * always be implemented regardless of what is specified here.
183 * @param interfaces array of interfaces to implement, or null
186 public void setInterfaces(Class
[] interfaces
) {
187 this.interfaces
= interfaces
;
191 * Set the {@link CallbackFilter} used to map the generated class' methods
192 * to a particular callback index.
193 * New object instances will always use the same mapping, but may use different
194 * actual callback objects.
195 * @param filter the callback filter to use when generating a new class
198 public void setCallbackFilter(CallbackFilter filter
) {
199 this.filter
= filter
;
204 * Set the single {@link Callback} to use.
205 * Ignored if you use {@link #createClass}.
206 * @param callback the callback to use for all methods
209 public void setCallback(final Callback callback
) {
210 setCallbacks(new Callback
[]{ callback
});
214 * Set the array of callbacks to use.
215 * Ignored if you use {@link #createClass}.
216 * You must use a {@link CallbackFilter} to specify the index into this
217 * array for each method in the proxied class.
218 * @param callbacks the callback array
219 * @see #setCallbackFilter
222 public void setCallbacks(Callback
[] callbacks
) {
223 if (callbacks
!= null && callbacks
.length
== 0) {
224 throw new IllegalArgumentException("Array cannot be empty");
226 this.callbacks
= callbacks
;
230 * Set whether the enhanced object instances should implement
231 * the {@link Factory} interface.
232 * This was added for tools that need for proxies to be more
233 * indistinguishable from their targets. Also, in some cases it may
234 * be necessary to disable the <code>Factory</code> interface to
235 * prevent code from changing the underlying callbacks.
236 * @param useFactory whether to implement <code>Factory</code>; default is <code>true</code>
238 public void setUseFactory(boolean useFactory
) {
239 this.useFactory
= useFactory
;
243 * Set whether methods called from within the proxy's constructer
244 * will be intercepted. The default value is true. Unintercepted methods
245 * will call the method of the proxy's base class, if it exists.
246 * @param interceptDuringConstruction whether to intercept methods called from the constructor
248 public void setInterceptDuringConstruction(boolean interceptDuringConstruction
) {
249 this.interceptDuringConstruction
= interceptDuringConstruction
;
253 * Set the single type of {@link Callback} to use.
254 * This may be used instead of {@link #setCallback} when calling
255 * {@link #createClass}, since it may not be possible to have
256 * an array of actual callback instances.
257 * @param callbackType the type of callback to use for all methods
258 * @see #setCallbackTypes
260 public void setCallbackType(Class callbackType
) {
261 setCallbackTypes(new Class
[]{ callbackType
});
265 * Set the array of callback types to use.
266 * This may be used instead of {@link #setCallbacks} when calling
267 * {@link #createClass}, since it may not be possible to have
268 * an array of actual callback instances.
269 * You must use a {@link CallbackFilter} to specify the index into this
270 * array for each method in the proxied class.
271 * @param callbackTypes the array of callback types
273 public void setCallbackTypes(Class
[] callbackTypes
) {
274 if (callbackTypes
!= null && callbackTypes
.length
== 0) {
275 throw new IllegalArgumentException("Array cannot be empty");
277 this.callbackTypes
= CallbackInfo
.determineTypes(callbackTypes
);
281 * Generate a new class if necessary and uses the specified
282 * callbacks (if any) to create a new object instance.
283 * Uses the no-arg constructor of the superclass.
284 * @return a new instance
286 public Object
create() {
288 argumentTypes
= null;
289 return createHelper();
293 * Generate a new class if necessary and uses the specified
294 * callbacks (if any) to create a new object instance.
295 * Uses the constructor of the superclass matching the <code>argumentTypes</code>
296 * parameter, with the given arguments.
297 * @param argumentTypes constructor signature
298 * @param arguments compatible wrapped arguments to pass to constructor
299 * @return a new instance
301 public Object
create(Class
[] argumentTypes
, Object
[] arguments
) {
303 if (argumentTypes
== null || arguments
== null || argumentTypes
.length
!= arguments
.length
) {
304 throw new IllegalArgumentException("Arguments must be non-null and of equal length");
306 synchronized (this) {
307 this.argumentTypes
= argumentTypes
;
308 this.arguments
= arguments
;
310 return createHelper();
313 this.arguments
= null;
319 * Generate a new class if necessary and return it without creating a new instance.
320 * This ignores any callbacks that have been set.
321 * To create a new instance you will have to use reflection, and methods
322 * called during the constructor will not be intercepted. To avoid this problem,
323 * use the multi-arg <code>create</code> method.
324 * @see #create(Class[], Object[])
326 public Class
createClass() {
328 return (Class
)createHelper();
332 * Insert a static serialVersionUID field into the generated class.
333 * @param sUID the field value, or null to avoid generating field.
335 public void setSerialVersionUID(Long sUID
) {
336 serialVersionUID
= sUID
;
339 private void validate() {
340 if (classOnly ^
(callbacks
== null)) {
342 throw new IllegalStateException("createClass does not accept callbacks");
344 throw new IllegalStateException("Callbacks are required");
347 if (classOnly
&& (callbackTypes
== null)) {
348 throw new IllegalStateException("Callback types are required");
350 if (callbacks
!= null && callbackTypes
!= null) {
351 if (callbacks
.length
!= callbackTypes
.length
) {
352 throw new IllegalStateException("Lengths of callback and callback types array must be the same");
354 Type
[] check
= CallbackInfo
.determineTypes(callbacks
);
355 for (int i
= 0; i
< check
.length
; i
++) {
356 if (!check
[i
].equals(callbackTypes
[i
])) {
357 throw new IllegalStateException("Callback " + check
[i
] + " is not assignable to " + callbackTypes
[i
]);
360 } else if (callbacks
!= null) {
361 callbackTypes
= CallbackInfo
.determineTypes(callbacks
);
363 if (filter
== null) {
364 if (callbackTypes
.length
> 1) {
365 throw new IllegalStateException("Multiple callback types possible but no filter specified");
369 if (interfaces
!= null) {
370 for (Class anInterface
: interfaces
) {
371 if (anInterface
== null) {
372 throw new IllegalStateException("Interfaces cannot be null");
374 if (!anInterface
.isInterface()) {
375 throw new IllegalStateException(anInterface
+ " is not an interface");
381 private Object
createHelper() {
383 if (superclass
!= null) {
384 setNamePrefix(superclass
.getName());
385 } else if (interfaces
!= null) {
386 setNamePrefix(interfaces
[ReflectUtils
.findPackageProtected(interfaces
)].getName());
388 return super.create(KEY_FACTORY
.newInstance((superclass
!= null) ? superclass
.getName() : null,
389 ReflectUtils
.getNames(interfaces
),
393 interceptDuringConstruction
,
397 protected ClassLoader
getDefaultClassLoader() {
399 ClassLoader bestLoader
= null;
400 if (interfaces
!= null && interfaces
.length
> 0) {
401 for (final Class anInterface
: interfaces
) {
402 final ClassLoader loader
= anInterface
.getClassLoader();
403 if (loader
instanceof PluginClassLoader
) {
404 final int order
= PluginManager
.getPluginLoadingOrder(((PluginClassLoader
)loader
).getPluginId());
405 if (maxIndex
< order
) {
412 ClassLoader superLoader
= null;
413 if (superclass
!= null) {
414 superLoader
= superclass
.getClassLoader();
415 if (superLoader
instanceof PluginClassLoader
&&
416 maxIndex
< PluginManager
.getPluginLoadingOrder(((PluginClassLoader
)superLoader
).getPluginId())) {
420 if (bestLoader
!= null) return bestLoader
;
424 private static Signature
rename(Signature sig
, int index
) {
425 return new Signature("CGLIB$" + sig
.getName() + "$" + index
,
426 sig
.getDescriptor());
430 * Finds all of the methods that will be extended by an
431 * Enhancer-generated class using the specified superclass and
432 * interfaces. This can be useful in building a list of Callback
433 * objects. The methods are added to the end of the given list. Due
434 * to the subclassing nature of the classes generated by Enhancer,
435 * the methods are guaranteed to be non-static, non-final, and
436 * non-private. Each method signature will only occur once, even if
437 * it occurs in multiple classes.
438 * @param superclass the class that will be extended, or null
439 * @param interfaces the list of interfaces that will be implemented, or null
440 * @param methods the list into which to copy the applicable methods
442 public static void getMethods(Class superclass
, Class
[] interfaces
, List
<Method
> methods
)
444 getMethods(superclass
, interfaces
, methods
, null, null);
447 private static void getMethods(Class superclass
, Class
[] interfaces
, List
<Method
> methods
, List
<Method
> interfaceMethods
, Set forcePublic
)
449 ReflectUtils
.addAllMethods(superclass
, methods
);
450 List
<Method
> target
= (interfaceMethods
!= null) ? interfaceMethods
: methods
;
451 if (interfaces
!= null) {
452 for (Class anInterface
: interfaces
) {
453 if (anInterface
!= Factory
.class) {
454 ReflectUtils
.addAllMethods(anInterface
, target
);
458 if (interfaceMethods
!= null) {
459 if (forcePublic
!= null) {
460 forcePublic
.addAll(MethodWrapper
.createSet(interfaceMethods
));
462 methods
.addAll(interfaceMethods
);
464 CollectionUtils
.filter(methods
, new RejectModifierPredicate(Constants
.ACC_STATIC
| Constants
.ACC_FINAL
));
465 CollectionUtils
.filter(methods
, new VisibilityPredicate(superclass
, true));
466 CollectionUtils
.filter(methods
, new DuplicatesPredicate());
469 public void generateClass(ClassVisitor v
) throws Exception
{
470 Class sc
= (superclass
== null) ? Object
.class : superclass
;
472 if (TypeUtils
.isFinal(sc
.getModifiers())) {
473 throw new IllegalArgumentException("Cannot subclass final class " + sc
);
475 List
<Constructor
> constructors
= new ArrayList
<Constructor
>(Arrays
.asList(sc
.getDeclaredConstructors()));
476 filterConstructors(sc
, constructors
);
478 // Order is very important: must add superclass, then
479 // its superclass chain, then each interface and
480 // its superinterfaces.
481 final Set forcePublic
= new HashSet();
482 List
<Method
> actualMethods
= new ArrayList
<Method
>();
483 final Map
<Method
, Method
> covariantMethods
= new HashMap
<Method
, Method
>();
484 getMethods(sc
, interfaces
, actualMethods
, new ArrayList
<Method
>(), forcePublic
);
486 //Changes by Peter Gromov & Gregory Shrago
488 for(Class aClass
= sc
; aClass
!= null; aClass
= aClass
.getSuperclass()) {
489 for (final Method method
: aClass
.getDeclaredMethods()) {
490 if (actualMethods
.contains(method
)) {
491 removeAllCovariantMethods(actualMethods
, method
, covariantMethods
);
497 ClassEmitter e
= new ClassEmitter(v
);
498 e
.begin_class(Constants
.V1_2
,
499 Constants
.ACC_PUBLIC
,
503 TypeUtils
.add(TypeUtils
.getTypes(interfaces
), FACTORY
) :
504 TypeUtils
.getTypes(interfaces
)),
505 Constants
.SOURCE_FILE
);
506 List constructorInfo
= CollectionUtils
.transform(constructors
, MethodInfoTransformer
.getInstance());
508 e
.declare_field(Constants
.ACC_PRIVATE
, BOUND_FIELD
, Type
.BOOLEAN_TYPE
, null);
509 if (!interceptDuringConstruction
) {
510 e
.declare_field(Constants
.ACC_PRIVATE
, CONSTRUCTED_FIELD
, Type
.BOOLEAN_TYPE
, null);
512 e
.declare_field(Constants
.PRIVATE_FINAL_STATIC
, THREAD_CALLBACKS_FIELD
, THREAD_LOCAL
, null);
513 e
.declare_field(Constants
.PRIVATE_FINAL_STATIC
, STATIC_CALLBACKS_FIELD
, CALLBACK_ARRAY
, null);
514 if (serialVersionUID
!= null) {
515 e
.declare_field(Constants
.PRIVATE_FINAL_STATIC
, Constants
.SUID_FIELD_NAME
, Type
.LONG_TYPE
, serialVersionUID
);
518 for (int i
= 0; i
< callbackTypes
.length
; i
++) {
519 e
.declare_field(Constants
.ACC_PRIVATE
, getCallbackField(i
), callbackTypes
[i
], null);
521 final Map
<Method
, MethodInfo
> methodInfoMap
= new HashMap
<Method
, MethodInfo
>();
522 for (Method method
: actualMethods
) {
524 Constants
.ACC_FINAL
| (method
.getModifiers() & ~Constants
.ACC_ABSTRACT
& ~Constants
.ACC_NATIVE
& ~Constants
.ACC_SYNCHRONIZED
);
525 if (forcePublic
.contains(MethodWrapper
.create(method
))) {
526 modifiers
= (modifiers
& ~Constants
.ACC_PROTECTED
) | Constants
.ACC_PUBLIC
;
528 if (covariantMethods
.containsKey(method
)) {
529 modifiers
= modifiers
| Constants
.ACC_BRIDGE
;
531 methodInfoMap
.put(method
, ReflectUtils
.getMethodInfo(method
, modifiers
));
534 emitMethods(e
, methodInfoMap
, covariantMethods
);
535 emitConstructors(e
, constructorInfo
);
536 emitSetThreadCallbacks(e
);
537 emitSetStaticCallbacks(e
);
538 emitBindCallbacks(e
);
541 int[] keys
= getCallbackKeys();
542 emitNewInstanceCallbacks(e
);
543 emitNewInstanceCallback(e
);
544 emitNewInstanceMultiarg(e
, constructorInfo
);
545 emitGetCallback(e
, keys
);
546 emitSetCallback(e
, keys
);
554 private static void removeAllCovariantMethods(final List
<Method
> actualMethods
, final Method method
, final Map
<Method
, Method
> covariantMethods
) {
555 if ((method
.getModifiers() & Constants
.ACC_SYNTHETIC
) != 0) {
559 for (Iterator
<Method
> it
= actualMethods
.iterator(); it
.hasNext();) {
560 Method actualMethod
= it
.next();
561 if (actualMethod
.equals(method
)) {
565 if (!actualMethod
.getName().equals(method
.getName()) ||
566 !Arrays
.equals(actualMethod
.getParameterTypes(), method
.getParameterTypes())) {
570 if (ReflectionCache
.isAssignable(actualMethod
.getReturnType(), method
.getReturnType())) {
571 if ((actualMethod
.getModifiers() & Constants
.ACC_ABSTRACT
) != 0 || (actualMethod
.getModifiers() & Constants
.ACC_SYNTHETIC
) != 0) {
572 covariantMethods
.put(actualMethod
, method
); //generate bridge
582 * Filter the list of constructors from the superclass. The
583 * constructors which remain will be included in the generated
584 * class. The default implementation is to filter out all private
585 * constructors, but subclasses may extend Enhancer to override this
587 * @param sc the superclass
588 * @param constructors the list of all declared constructors from the superclass
589 * @throws IllegalArgumentException if there are no non-private constructors
591 protected void filterConstructors(Class sc
, List
<Constructor
> constructors
) {
592 CollectionUtils
.filter(constructors
, new VisibilityPredicate(sc
, true));
593 if (constructors
.size() == 0) {
594 throw new IllegalArgumentException("No visible constructors in " + sc
);
598 protected Object
firstInstance(Class type
) throws Exception
{
602 return createUsingReflection(type
);
606 protected Object
nextInstance(Object instance
) {
607 Class protoclass
= (instance
instanceof Class
) ?
(Class
) instance
: instance
.getClass();
610 } else if (instance
instanceof Factory
) {
611 if (argumentTypes
!= null) {
612 return ((Factory
)instance
).newInstance(argumentTypes
, arguments
, callbacks
);
614 return ((Factory
)instance
).newInstance(callbacks
);
617 return createUsingReflection(protoclass
);
622 * Call this method to register the {@link Callback} array to use before
623 * creating a new instance of the generated class via reflection. If you are using
624 * an instance of <code>Enhancer</code> or the {@link Factory} interface to create
625 * new instances, this method is unnecessary. Its primary use is for when you want to
626 * cache and reuse a generated class yourself, and the generated class does
627 * <i>not</i> implement the {@link Factory} interface.
629 * Note that this method only registers the callbacks on the current thread.
630 * If you want to register callbacks for instances created by multiple threads,
631 * use {@link #registerStaticCallbacks}.
633 * The registered callbacks are overwritten and subsequently cleared
634 * when calling any of the <code>create</code> methods (such as
636 * @param generatedClass a class previously created by {@link Enhancer}
637 * @param callbacks the array of callbacks to use when instances of the generated
639 * @see #setUseFactory
641 public static void registerCallbacks(Class generatedClass
, Callback
[] callbacks
) {
642 setThreadCallbacks(generatedClass
, callbacks
);
646 * Similar to {@link #registerCallbacks}, but suitable for use
647 * when multiple threads will be creating instances of the generated class.
648 * The thread-level callbacks will always override the static callbacks.
649 * Static callbacks are never cleared.
650 * @param generatedClass a class previously created by {@link Enhancer}
651 * @param callbacks the array of callbacks to use when instances of the generated
654 public static void registerStaticCallbacks(Class generatedClass
, Callback
[] callbacks
) {
655 setCallbacksHelper(generatedClass
, callbacks
, SET_STATIC_CALLBACKS_NAME
);
659 * Determine if a class was generated using <code>Enhancer</code>.
660 * @param type any class
661 * @return whether the class was generated using <code>Enhancer</code>
663 public static boolean isEnhanced(Class type
) {
665 getCallbacksSetter(type
, SET_THREAD_CALLBACKS_NAME
);
667 } catch (NoSuchMethodException e
) {
672 private static void setThreadCallbacks(Class type
, Callback
[] callbacks
) {
673 setCallbacksHelper(type
, callbacks
, SET_THREAD_CALLBACKS_NAME
);
676 private static void setCallbacksHelper(Class type
, Callback
[] callbacks
, String methodName
) {
679 Method setter
= getCallbacksSetter(type
, methodName
);
680 setter
.invoke(null, (Object
)callbacks
);
681 } catch (NoSuchMethodException e
) {
682 throw new IllegalArgumentException(type
+ " is not an enhanced class");
683 } catch (IllegalAccessException e
) {
684 throw new CodeGenerationException(e
);
685 } catch (InvocationTargetException e
) {
686 throw new CodeGenerationException(e
);
690 private static Method
getCallbacksSetter(Class type
, String methodName
) throws NoSuchMethodException
{
691 return type
.getDeclaredMethod(methodName
, Callback
[].class);
694 private Object
createUsingReflection(Class type
) {
695 setThreadCallbacks(type
, callbacks
);
698 if (argumentTypes
!= null) {
700 return ReflectUtils
.newInstance(type
, argumentTypes
, arguments
);
704 return ReflectUtils
.newInstance(type
);
708 // clear thread callbacks to allow them to be gc'd
709 setThreadCallbacks(type
, null);
714 * Helper method to create an intercepted object.
715 * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
716 * instead of this static method.
717 * @param type class to extend or interface to implement
718 * @param callback the callback to use for all methods
720 public static Object
create(Class type
, Callback callback
) {
721 Enhancer e
= new Enhancer();
722 e
.setSuperclass(type
);
723 e
.setCallback(callback
);
728 * Helper method to create an intercepted object.
729 * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
730 * instead of this static method.
731 * @param type class to extend or interface to implement
732 * @param interfaces array of interfaces to implement, or null
733 * @param callback the callback to use for all methods
735 public static Object
create(Class superclass
, Class interfaces
[], Callback callback
) {
736 Enhancer e
= new Enhancer();
737 e
.setSuperclass(superclass
);
738 e
.setInterfaces(interfaces
);
739 e
.setCallback(callback
);
744 * Helper method to create an intercepted object.
745 * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
746 * instead of this static method.
747 * @param type class to extend or interface to implement
748 * @param interfaces array of interfaces to implement, or null
749 * @param filter the callback filter to use when generating a new class
750 * @param callbacks callback implementations to use for the enhanced object
752 public static Object
create(Class superclass
, Class
[] interfaces
, CallbackFilter filter
, Callback
[] callbacks
) {
753 Enhancer e
= new Enhancer();
754 e
.setSuperclass(superclass
);
755 e
.setInterfaces(interfaces
);
756 e
.setCallbackFilter(filter
);
757 e
.setCallbacks(callbacks
);
761 private void emitConstructors(ClassEmitter ce
, List constructors
) {
762 boolean seenNull
= false;
763 for (final Object constructor1
: constructors
) {
764 MethodInfo constructor
= (MethodInfo
)constructor1
;
765 CodeEmitter e
= EmitUtils
.begin_method(ce
, constructor
, Constants
.ACC_PUBLIC
);
769 Signature sig
= constructor
.getSignature();
770 seenNull
= seenNull
|| sig
.getDescriptor().equals("()V");
771 e
.super_invoke_constructor(sig
);
772 e
.invoke_static_this(BIND_CALLBACKS
);
773 if (!interceptDuringConstruction
) {
776 e
.putfield(CONSTRUCTED_FIELD
);
781 if (!classOnly
&& !seenNull
&& arguments
== null) {
782 throw new IllegalArgumentException("Superclass has no null constructors but no arguments were given");
786 private int[] getCallbackKeys() {
787 int[] keys
= new int[callbackTypes
.length
];
788 for (int i
= 0; i
< callbackTypes
.length
; i
++) {
794 private void emitGetCallback(ClassEmitter ce
, int[] keys
) {
795 final CodeEmitter e
= ce
.begin_method(Constants
.ACC_PUBLIC
, GET_CALLBACK
, null);
797 e
.invoke_static_this(BIND_CALLBACKS
);
800 e
.process_switch(keys
, new ProcessSwitchCallback() {
801 public void processCase(int key
, Label end
) {
802 e
.getfield(getCallbackField(key
));
805 public void processDefault() {
806 e
.pop(); // stack height
814 private void emitSetCallback(ClassEmitter ce
, int[] keys
) {
815 final CodeEmitter e
= ce
.begin_method(Constants
.ACC_PUBLIC
, SET_CALLBACK
, null);
819 e
.process_switch(keys
, new ProcessSwitchCallback() {
820 public void processCase(int key
, Label end
) {
821 e
.checkcast(callbackTypes
[key
]);
822 e
.putfield(getCallbackField(key
));
825 public void processDefault() {
826 final Type type
= Type
.getType(AssertionError
.class);
827 e
.new_instance(type
);
829 e
.invoke_constructor(type
);
837 private void emitSetCallbacks(ClassEmitter ce
) {
838 CodeEmitter e
= ce
.begin_method(Constants
.ACC_PUBLIC
, SET_CALLBACKS
, null);
841 for (int i
= 0; i
< callbackTypes
.length
; i
++) {
844 e
.checkcast(callbackTypes
[i
]);
845 e
.putfield(getCallbackField(i
));
851 private void emitGetCallbacks(ClassEmitter ce
) {
852 CodeEmitter e
= ce
.begin_method(Constants
.ACC_PUBLIC
, GET_CALLBACKS
, null);
854 e
.invoke_static_this(BIND_CALLBACKS
);
856 e
.push(callbackTypes
.length
);
857 e
.newarray(CALLBACK
);
858 for (int i
= 0; i
< callbackTypes
.length
; i
++) {
862 e
.getfield(getCallbackField(i
));
869 private void emitNewInstanceCallbacks(ClassEmitter ce
) {
870 CodeEmitter e
= ce
.begin_method(Constants
.ACC_PUBLIC
, NEW_INSTANCE
, null);
872 e
.invoke_static_this(SET_THREAD_CALLBACKS
);
873 emitCommonNewInstance(e
);
876 private void emitCommonNewInstance(CodeEmitter e
) {
877 e
.new_instance_this();
879 e
.invoke_constructor_this();
881 e
.invoke_static_this(SET_THREAD_CALLBACKS
);
886 private void emitNewInstanceCallback(ClassEmitter ce
) {
887 CodeEmitter e
= ce
.begin_method(Constants
.ACC_PUBLIC
, SINGLE_NEW_INSTANCE
, null);
888 switch (callbackTypes
.length
) {
890 // TODO: make sure Callback is null
893 // for now just make a new array; TODO: optimize
895 e
.newarray(CALLBACK
);
900 e
.invoke_static_this(SET_THREAD_CALLBACKS
);
903 e
.throw_exception(ILLEGAL_STATE_EXCEPTION
, "More than one callback object required");
905 emitCommonNewInstance(e
);
908 private void emitNewInstanceMultiarg(ClassEmitter ce
, List constructors
) {
909 final CodeEmitter e
= ce
.begin_method(Constants
.ACC_PUBLIC
, MULTIARG_NEW_INSTANCE
, null);
911 e
.invoke_static_this(SET_THREAD_CALLBACKS
);
912 e
.new_instance_this();
915 EmitUtils
.constructor_switch(e
, constructors
, new ObjectSwitchCallback() {
916 public void processCase(Object key
, Label end
) {
917 MethodInfo constructor
= (MethodInfo
)key
;
918 Type types
[] = constructor
.getSignature().getArgumentTypes();
919 for (int i
= 0; i
< types
.length
; i
++) {
925 e
.invoke_constructor_this(constructor
.getSignature());
928 public void processDefault() {
929 e
.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION
, "Constructor not found");
933 e
.invoke_static_this(SET_THREAD_CALLBACKS
);
938 private void emitMethods(final ClassEmitter ce
, Map
<Method
, MethodInfo
> methodMap
, final Map
<Method
, Method
> covariantMethods
) {
939 CallbackGenerator
[] generators
= CallbackInfo
.getGenerators(callbackTypes
);
940 Map
<MethodInfo
, MethodInfo
> covariantInfoMap
= new HashMap
<MethodInfo
, MethodInfo
>();
941 for (Method method
: methodMap
.keySet()) {
942 final Method delegate
= covariantMethods
.get(method
);
943 if (delegate
!= null) {
944 covariantInfoMap
.put(methodMap
.get(method
), ReflectUtils
.getMethodInfo(delegate
, delegate
.getModifiers()));
947 BridgeMethodGenerator bridgeMethodGenerator
= new BridgeMethodGenerator(covariantInfoMap
);
949 Map
<CallbackGenerator
,List
<MethodInfo
>> groups
= new HashMap
<CallbackGenerator
, List
<MethodInfo
>>();
950 final Map
<MethodInfo
,Integer
> indexes
= new HashMap
<MethodInfo
, Integer
>();
951 final Map
<MethodInfo
,Integer
> originalModifiers
= new HashMap
<MethodInfo
, Integer
>();
952 final Map positions
= CollectionUtils
.getIndexMap(new ArrayList
<MethodInfo
>(methodMap
.values()));
954 for (Method actualMethod
: methodMap
.keySet()) {
955 MethodInfo method
= methodMap
.get(actualMethod
);
956 int index
= filter
.accept(actualMethod
);
957 if (index
>= callbackTypes
.length
) {
958 throw new IllegalArgumentException("Callback filter returned an index that is too large: " + index
);
960 originalModifiers
.put(method
, (actualMethod
!= null) ? actualMethod
.getModifiers() : method
.getModifiers());
961 indexes
.put(method
, index
);
962 final CallbackGenerator generator
= covariantMethods
.containsKey(actualMethod
)? bridgeMethodGenerator
: generators
[index
];
963 List
<MethodInfo
> group
= groups
.get(generator
);
965 groups
.put(generator
, group
= new ArrayList
<MethodInfo
>(methodMap
.size()));
970 CodeEmitter se
= ce
.getStaticHook();
971 se
.new_instance(THREAD_LOCAL
);
973 se
.invoke_constructor(THREAD_LOCAL
, CSTRUCT_NULL
);
974 se
.putfield(THREAD_CALLBACKS_FIELD
);
976 CallbackGenerator
.Context context
= new CallbackGenerator
.Context() {
977 public ClassLoader
getClassLoader() {
978 return AdvancedEnhancer
.this.getClassLoader();
980 public int getOriginalModifiers(MethodInfo method
) {
981 return originalModifiers
.get(method
);
983 public int getIndex(MethodInfo method
) {
984 return indexes
.get(method
);
986 public void emitCallback(CodeEmitter e
, int index
) {
987 emitCurrentCallback(e
, index
);
989 public Signature
getImplSignature(MethodInfo method
) {
990 return rename(method
.getSignature(), (Integer
)positions
.get(method
));
992 public CodeEmitter
beginMethod(ClassEmitter ce
, MethodInfo method
) {
993 CodeEmitter e
= EmitUtils
.begin_method(ce
, method
);
994 if (!interceptDuringConstruction
&&
995 !TypeUtils
.isAbstract(method
.getModifiers())) {
996 Label constructed
= e
.make_label();
998 e
.getfield(CONSTRUCTED_FIELD
);
999 e
.if_jump(e
.NE
, constructed
);
1004 e
.mark(constructed
);
1009 Set
<CallbackGenerator
> seenGen
= new HashSet
<CallbackGenerator
>();
1010 for (int i
= 0; i
< callbackTypes
.length
+ 1; i
++) {
1011 CallbackGenerator gen
= i
== callbackTypes
.length? bridgeMethodGenerator
: generators
[i
];
1012 if (!seenGen
.contains(gen
)) {
1014 final List
<MethodInfo
> fmethods
= groups
.get(gen
);
1015 if (fmethods
!= null) {
1017 gen
.generate(ce
, context
, fmethods
);
1018 gen
.generateStatic(se
, context
, fmethods
);
1019 } catch (RuntimeException x
) {
1021 } catch (Exception x
) {
1022 throw new CodeGenerationException(x
);
1031 private void emitSetThreadCallbacks(ClassEmitter ce
) {
1032 CodeEmitter e
= ce
.begin_method(Constants
.ACC_PUBLIC
| Constants
.ACC_STATIC
,
1033 SET_THREAD_CALLBACKS
,
1035 e
.getfield(THREAD_CALLBACKS_FIELD
);
1037 e
.invoke_virtual(THREAD_LOCAL
, THREAD_LOCAL_SET
);
1042 private void emitSetStaticCallbacks(ClassEmitter ce
) {
1043 CodeEmitter e
= ce
.begin_method(Constants
.ACC_PUBLIC
| Constants
.ACC_STATIC
,
1044 SET_STATIC_CALLBACKS
,
1047 e
.putfield(STATIC_CALLBACKS_FIELD
);
1052 private void emitCurrentCallback(CodeEmitter e
, int index
) {
1054 e
.getfield(getCallbackField(index
));
1056 Label end
= e
.make_label();
1058 e
.pop(); // stack height
1060 e
.invoke_static_this(BIND_CALLBACKS
);
1062 e
.getfield(getCallbackField(index
));
1066 private void emitBindCallbacks(ClassEmitter ce
) {
1067 CodeEmitter e
= ce
.begin_method(Constants
.PRIVATE_FINAL_STATIC
,
1070 Local me
= e
.make_local();
1075 Label end
= e
.make_label();
1077 e
.getfield(BOUND_FIELD
);
1078 e
.if_jump(e
.NE
, end
);
1081 e
.putfield(BOUND_FIELD
);
1083 e
.getfield(THREAD_CALLBACKS_FIELD
);
1084 e
.invoke_virtual(THREAD_LOCAL
, THREAD_LOCAL_GET
);
1086 Label found_callback
= e
.make_label();
1087 e
.ifnonnull(found_callback
);
1090 e
.getfield(STATIC_CALLBACKS_FIELD
);
1092 e
.ifnonnull(found_callback
);
1096 e
.mark(found_callback
);
1097 e
.checkcast(CALLBACK_ARRAY
);
1100 for (int i
= callbackTypes
.length
- 1; i
>= 0; i
--) {
1105 e
.checkcast(callbackTypes
[i
]);
1106 e
.putfield(getCallbackField(i
));
1114 private static String
getCallbackField(int index
) {
1115 return "CGLIB$CALLBACK_" + index
;