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