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 }