Make it build with cglib-2.2
[fedora-idea.git] / platform / platform-impl / src / net / sf / cglib / proxy / AdvancedEnhancer.java
blob340011261ccfed317a500323bc0fdbfc52eb97a9
1 /*
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;
29 import java.util.*;
31 /**
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
38 * implementations.
39 * <p>
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.
44 * <p>
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}.
50 * <p>
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.
54 * <p>
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.
59 * <p>
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) {
68 return 0;
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,
107 CALLBACK_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,
129 String[] interfaces,
130 CallbackFilter filter,
131 Type[] callbackTypes,
132 boolean useFactory,
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.
154 * @see Factory
156 public AdvancedEnhancer() {
157 super(SOURCE);
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;
175 } else {
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
184 * @see Factory
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
196 * @see #setCallbacks
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
207 * @see #setCallbacks
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
220 * @see #setCallback
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() {
287 classOnly = false;
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) {
302 classOnly = false;
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;
309 try {
310 return createHelper();
312 finally {
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() {
327 classOnly = true;
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)) {
341 if (classOnly) {
342 throw new IllegalStateException("createClass does not accept callbacks");
343 } else {
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");
367 filter = ALL_ZERO;
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() {
382 validate();
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),
390 filter,
391 callbackTypes,
392 useFactory,
393 interceptDuringConstruction,
394 serialVersionUID));
397 protected ClassLoader getDefaultClassLoader() {
398 int maxIndex = -1;
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) {
406 maxIndex = order;
407 bestLoader = loader;
412 ClassLoader superLoader = null;
413 if (superclass != null) {
414 superLoader = superclass.getClassLoader();
415 if (superLoader instanceof PluginClassLoader &&
416 maxIndex < PluginManager.getPluginLoadingOrder(((PluginClassLoader)superLoader).getPluginId())) {
417 return superLoader;
420 if (bestLoader != null) return bestLoader;
421 return superLoader;
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,
500 getClassName(),
501 Type.getType(sc),
502 (useFactory ?
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) {
523 int modifiers =
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);
540 if (useFactory) {
541 int[] keys = getCallbackKeys();
542 emitNewInstanceCallbacks(e);
543 emitNewInstanceCallback(e);
544 emitNewInstanceMultiarg(e, constructorInfo);
545 emitGetCallback(e, keys);
546 emitSetCallback(e, keys);
547 emitGetCallbacks(e);
548 emitSetCallbacks(e);
551 e.end_class();
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) {
556 return;
559 for (Iterator<Method> it = actualMethods.iterator(); it.hasNext();) {
560 Method actualMethod = it.next();
561 if (actualMethod.equals(method)) {
562 continue;
565 if (!actualMethod.getName().equals(method.getName()) ||
566 !Arrays.equals(actualMethod.getParameterTypes(), method.getParameterTypes())) {
567 continue;
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
574 else {
575 it.remove();
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
586 * behavior.
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 {
599 if (classOnly) {
600 return type;
601 } else {
602 return createUsingReflection(type);
606 protected Object nextInstance(Object instance) {
607 Class protoclass = (instance instanceof Class) ? (Class) instance : instance.getClass();
608 if (classOnly) {
609 return protoclass;
610 } else if (instance instanceof Factory) {
611 if (argumentTypes != null) {
612 return ((Factory)instance).newInstance(argumentTypes, arguments, callbacks);
613 } else {
614 return ((Factory)instance).newInstance(callbacks);
616 } else {
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.
628 * <p>
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}.
632 * <p>
633 * The registered callbacks are overwritten and subsequently cleared
634 * when calling any of the <code>create</code> methods (such as
635 * {@link #create}).
636 * @param generatedClass a class previously created by {@link Enhancer}
637 * @param callbacks the array of callbacks to use when instances of the generated
638 * class are created
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
652 * class are created
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) {
664 try {
665 getCallbacksSetter(type, SET_THREAD_CALLBACKS_NAME);
666 return true;
667 } catch (NoSuchMethodException e) {
668 return false;
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) {
677 // TODO: optimize
678 try {
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);
696 try{
698 if (argumentTypes != null) {
700 return ReflectUtils.newInstance(type, argumentTypes, arguments);
702 } else {
704 return ReflectUtils.newInstance(type);
707 }finally{
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);
724 return e.create();
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);
740 return e.create();
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);
758 return e.create();
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);
766 e.load_this();
767 e.dup();
768 e.load_args();
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) {
774 e.load_this();
775 e.push(1);
776 e.putfield(CONSTRUCTED_FIELD);
778 e.return_value();
779 e.end_method();
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++) {
789 keys[i] = i;
791 return keys;
794 private void emitGetCallback(ClassEmitter ce, int[] keys) {
795 final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, GET_CALLBACK, null);
796 e.load_this();
797 e.invoke_static_this(BIND_CALLBACKS);
798 e.load_this();
799 e.load_arg(0);
800 e.process_switch(keys, new ProcessSwitchCallback() {
801 public void processCase(int key, Label end) {
802 e.getfield(getCallbackField(key));
803 e.goTo(end);
805 public void processDefault() {
806 e.pop(); // stack height
807 e.aconst_null();
810 e.return_value();
811 e.end_method();
814 private void emitSetCallback(ClassEmitter ce, int[] keys) {
815 final CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, SET_CALLBACK, null);
816 e.load_this();
817 e.load_arg(1);
818 e.load_arg(0);
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));
823 e.goTo(end);
825 public void processDefault() {
826 final Type type = Type.getType(AssertionError.class);
827 e.new_instance(type);
828 e.dup();
829 e.invoke_constructor(type);
830 e.athrow();
833 e.return_value();
834 e.end_method();
837 private void emitSetCallbacks(ClassEmitter ce) {
838 CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, SET_CALLBACKS, null);
839 e.load_this();
840 e.load_arg(0);
841 for (int i = 0; i < callbackTypes.length; i++) {
842 e.dup2();
843 e.aaload(i);
844 e.checkcast(callbackTypes[i]);
845 e.putfield(getCallbackField(i));
847 e.return_value();
848 e.end_method();
851 private void emitGetCallbacks(ClassEmitter ce) {
852 CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, GET_CALLBACKS, null);
853 e.load_this();
854 e.invoke_static_this(BIND_CALLBACKS);
855 e.load_this();
856 e.push(callbackTypes.length);
857 e.newarray(CALLBACK);
858 for (int i = 0; i < callbackTypes.length; i++) {
859 e.dup();
860 e.push(i);
861 e.load_this();
862 e.getfield(getCallbackField(i));
863 e.aastore();
865 e.return_value();
866 e.end_method();
869 private void emitNewInstanceCallbacks(ClassEmitter ce) {
870 CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null);
871 e.load_arg(0);
872 e.invoke_static_this(SET_THREAD_CALLBACKS);
873 emitCommonNewInstance(e);
876 private void emitCommonNewInstance(CodeEmitter e) {
877 e.new_instance_this();
878 e.dup();
879 e.invoke_constructor_this();
880 e.aconst_null();
881 e.invoke_static_this(SET_THREAD_CALLBACKS);
882 e.return_value();
883 e.end_method();
886 private void emitNewInstanceCallback(ClassEmitter ce) {
887 CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, SINGLE_NEW_INSTANCE, null);
888 switch (callbackTypes.length) {
889 case 0:
890 // TODO: make sure Callback is null
891 break;
892 case 1:
893 // for now just make a new array; TODO: optimize
894 e.push(1);
895 e.newarray(CALLBACK);
896 e.dup();
897 e.push(0);
898 e.load_arg(0);
899 e.aastore();
900 e.invoke_static_this(SET_THREAD_CALLBACKS);
901 break;
902 default:
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);
910 e.load_arg(2);
911 e.invoke_static_this(SET_THREAD_CALLBACKS);
912 e.new_instance_this();
913 e.dup();
914 e.load_arg(0);
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++) {
920 e.load_arg(1);
921 e.push(i);
922 e.aaload();
923 e.unbox(types[i]);
925 e.invoke_constructor_this(constructor.getSignature());
926 e.goTo(end);
928 public void processDefault() {
929 e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Constructor not found");
932 e.aconst_null();
933 e.invoke_static_this(SET_THREAD_CALLBACKS);
934 e.return_value();
935 e.end_method();
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);
964 if (group == null) {
965 groups.put(generator, group = new ArrayList<MethodInfo>(methodMap.size()));
967 group.add(method);
970 CodeEmitter se = ce.getStaticHook();
971 se.new_instance(THREAD_LOCAL);
972 se.dup();
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();
997 e.load_this();
998 e.getfield(CONSTRUCTED_FIELD);
999 e.if_jump(e.NE, constructed);
1000 e.load_this();
1001 e.load_args();
1002 e.super_invoke();
1003 e.return_value();
1004 e.mark(constructed);
1006 return e;
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)) {
1013 seenGen.add(gen);
1014 final List<MethodInfo> fmethods = groups.get(gen);
1015 if (fmethods != null) {
1016 try {
1017 gen.generate(ce, context, fmethods);
1018 gen.generateStatic(se, context, fmethods);
1019 } catch (RuntimeException x) {
1020 throw x;
1021 } catch (Exception x) {
1022 throw new CodeGenerationException(x);
1027 se.return_value();
1028 se.end_method();
1031 private void emitSetThreadCallbacks(ClassEmitter ce) {
1032 CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC,
1033 SET_THREAD_CALLBACKS,
1034 null);
1035 e.getfield(THREAD_CALLBACKS_FIELD);
1036 e.load_arg(0);
1037 e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_SET);
1038 e.return_value();
1039 e.end_method();
1042 private void emitSetStaticCallbacks(ClassEmitter ce) {
1043 CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC,
1044 SET_STATIC_CALLBACKS,
1045 null);
1046 e.load_arg(0);
1047 e.putfield(STATIC_CALLBACKS_FIELD);
1048 e.return_value();
1049 e.end_method();
1052 private void emitCurrentCallback(CodeEmitter e, int index) {
1053 e.load_this();
1054 e.getfield(getCallbackField(index));
1055 e.dup();
1056 Label end = e.make_label();
1057 e.ifnonnull(end);
1058 e.pop(); // stack height
1059 e.load_this();
1060 e.invoke_static_this(BIND_CALLBACKS);
1061 e.load_this();
1062 e.getfield(getCallbackField(index));
1063 e.mark(end);
1066 private void emitBindCallbacks(ClassEmitter ce) {
1067 CodeEmitter e = ce.begin_method(Constants.PRIVATE_FINAL_STATIC,
1068 BIND_CALLBACKS,
1069 null);
1070 Local me = e.make_local();
1071 e.load_arg(0);
1072 e.checkcast_this();
1073 e.store_local(me);
1075 Label end = e.make_label();
1076 e.load_local(me);
1077 e.getfield(BOUND_FIELD);
1078 e.if_jump(e.NE, end);
1079 e.load_local(me);
1080 e.push(1);
1081 e.putfield(BOUND_FIELD);
1083 e.getfield(THREAD_CALLBACKS_FIELD);
1084 e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_GET);
1085 e.dup();
1086 Label found_callback = e.make_label();
1087 e.ifnonnull(found_callback);
1088 e.pop();
1090 e.getfield(STATIC_CALLBACKS_FIELD);
1091 e.dup();
1092 e.ifnonnull(found_callback);
1093 e.pop();
1094 e.goTo(end);
1096 e.mark(found_callback);
1097 e.checkcast(CALLBACK_ARRAY);
1098 e.load_local(me);
1099 e.swap();
1100 for (int i = callbackTypes.length - 1; i >= 0; i--) {
1101 if (i != 0) {
1102 e.dup2();
1104 e.aaload(i);
1105 e.checkcast(callbackTypes[i]);
1106 e.putfield(getCallbackField(i));
1109 e.mark(end);
1110 e.return_value();
1111 e.end_method();
1114 private static String getCallbackField(int index) {
1115 return "CGLIB$CALLBACK_" + index;