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