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