001// Licensed under the Apache License, Version 2.0 (the "License"); 002// you may not use this file except in compliance with the License. 003// You may obtain a copy of the License at 004// 005// http://www.apache.org/licenses/LICENSE-2.0 006// 007// Unless required by applicable law or agreed to in writing, software 008// distributed under the License is distributed on an "AS IS" BASIS, 009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 010// See the License for the specific language governing permissions and 011// limitations under the License. 012 013package org.apache.tapestry5.internal.plastic; 014 015import org.apache.tapestry5.internal.plastic.asm.AnnotationVisitor; 016import org.apache.tapestry5.internal.plastic.asm.Opcodes; 017import org.apache.tapestry5.internal.plastic.asm.Type; 018import org.apache.tapestry5.internal.plastic.asm.tree.*; 019import org.apache.tapestry5.plastic.*; 020 021import java.io.IOException; 022import java.lang.annotation.Annotation; 023import java.lang.reflect.Array; 024import java.lang.reflect.Constructor; 025import java.lang.reflect.Method; 026import java.lang.reflect.Modifier; 027import java.util.*; 028import java.util.regex.Matcher; 029import java.util.regex.Pattern; 030 031@SuppressWarnings("all") 032public class PlasticClassImpl extends Lockable implements PlasticClass, InternalPlasticClassTransformation, Opcodes 033{ 034 private static final String NOTHING_TO_VOID = "()V"; 035 036 static final String CONSTRUCTOR_NAME = "<init>"; 037 038 private static final String OBJECT_INT_TO_OBJECT = "(Ljava/lang/Object;I)Ljava/lang/Object;"; 039 040 private static final String OBJECT_INT_OBJECT_TO_VOID = "(Ljava/lang/Object;ILjava/lang/Object;)V"; 041 042 private static final String OBJECT_INT_OBJECT_ARRAY_TO_METHOD_INVOCATION_RESULT = String.format( 043 "(Ljava/lang/Object;I[Ljava/lang/Object;)%s", toDesc(Type.getInternalName(MethodInvocationResult.class))); 044 045 static final String ABSTRACT_METHOD_INVOCATION_INTERNAL_NAME = PlasticInternalUtils 046 .toInternalName(AbstractMethodInvocation.class.getName()); 047 048 private static final String HANDLE_SHIM_BASE_CLASS_INTERNAL_NAME = Type 049 .getInternalName(PlasticClassHandleShim.class); 050 051 static final String STATIC_CONTEXT_INTERNAL_NAME = Type.getInternalName(StaticContext.class); 052 053 private static final String INSTANCE_CONTEXT_INTERNAL_NAME = Type.getInternalName(InstanceContext.class); 054 055 private static final String INSTANCE_CONTEXT_DESC = toDesc(INSTANCE_CONTEXT_INTERNAL_NAME); 056 057 private static final String CONSTRUCTOR_DESC = String.format("(L%s;L%s;)V", STATIC_CONTEXT_INTERNAL_NAME, 058 INSTANCE_CONTEXT_INTERNAL_NAME); 059 060 static final Method STATIC_CONTEXT_GET_METHOD = toMethod(StaticContext.class, "get", int.class); 061 062 static final Method COMPUTED_VALUE_GET_METHOD = toMethod(ComputedValue.class, "get", InstanceContext.class); 063 064 private static final Method CONSTRUCTOR_CALLBACK_METHOD = toMethod(ConstructorCallback.class, "onConstruct", 065 Object.class, InstanceContext.class); 066 067 private static String toDesc(String internalName) 068 { 069 return "L" + internalName + ";"; 070 } 071 072 private static Method toMethod(Class declaringClass, String methodName, Class... parameterTypes) 073 { 074 return PlasticUtils.getMethod(declaringClass, methodName, parameterTypes); 075 } 076 077 static <T> T safeArrayDeref(T[] array, int index) 078 { 079 if (array == null) 080 return null; 081 082 return array[index]; 083 } 084 085 // Now past the inner classes; these are the instance variables of PlasticClassImpl proper: 086 087 final ClassNode classNode; 088 089 final PlasticClassPool pool; 090 091 private final boolean proxy; 092 093 final String className; 094 095 private final String superClassName; 096 097 private final AnnotationAccess annotationAccess; 098 099 // All the non-introduced (and non-constructor) methods, in sorted order 100 101 private final List<PlasticMethodImpl> methods; 102 103 private final Map<MethodDescription, PlasticMethod> description2method = new HashMap<MethodDescription, PlasticMethod>(); 104 105 final Set<String> methodNames = new HashSet<String>(); 106 107 private final List<ConstructorCallback> constructorCallbacks = PlasticInternalUtils.newList(); 108 109 // All non-introduced instance fields 110 111 private final List<PlasticFieldImpl> fields; 112 113 /** 114 * Methods that require special attention inside {@link #createInstantiator()} because they 115 * have method advice. 116 */ 117 final Set<PlasticMethodImpl> advisedMethods = PlasticInternalUtils.newSet(); 118 119 final NameCache nameCache = new NameCache(); 120 121 // This is generated from fields, as necessary 122 List<PlasticField> unclaimedFields; 123 124 private final Set<String> fieldNames = PlasticInternalUtils.newSet(); 125 126 final StaticContext staticContext; 127 128 final InheritanceData parentInheritanceData, inheritanceData; 129 130 // MethodNodes in which field transformations should occur; this is most existing and 131 // introduced methods, outside of special access methods. 132 133 final Set<MethodNode> fieldTransformMethods = PlasticInternalUtils.newSet(); 134 135 // Tracks any methods that the Shim class uses to gain access to fields; used to ensure that 136 // such methods are not optimized away incorrectly. 137 final Set<MethodNode> shimInvokedMethods = PlasticInternalUtils.newSet(); 138 139 140 /** 141 * Tracks instrumentations of fields of this class, including private fields which are not published into the 142 * {@link PlasticClassPool}. 143 */ 144 private final FieldInstrumentations fieldInstrumentations; 145 146 /** 147 * This normal no-arguments constructor, or null. By the end of the transformation 148 * this will be converted into an ordinary method. 149 */ 150 private MethodNode originalConstructor; 151 152 private final MethodNode newConstructor; 153 154 final InstructionBuilder constructorBuilder; 155 156 private String instanceContextFieldName; 157 158 private Class<?> transformedClass; 159 160 // Indexes used to identify fields or methods in the shim 161 int nextFieldIndex = 0; 162 163 int nextMethodIndex = 0; 164 165 // Set of fields that need to contribute to the shim and gain access to it 166 167 final Set<PlasticFieldImpl> shimFields = PlasticInternalUtils.newSet(); 168 169 // Set of methods that need to contribute to the shim and gain access to it 170 171 final Set<PlasticMethodImpl> shimMethods = PlasticInternalUtils.newSet(); 172 173 final ClassNode implementationClassNode; 174 175 private ClassNode interfaceClassNode; 176 177 /** 178 * @param classNode 179 * @param implementationClassNode 180 * @param pool 181 * @param parentInheritanceData 182 * @param parentStaticContext 183 * @param proxy 184 */ 185 public PlasticClassImpl(ClassNode classNode, ClassNode implementationClassNode, PlasticClassPool pool, InheritanceData parentInheritanceData, 186 StaticContext parentStaticContext, boolean proxy) 187 { 188 this.classNode = classNode; 189 this.pool = pool; 190 this.proxy = proxy; 191 this.implementationClassNode = implementationClassNode; 192 193 staticContext = parentStaticContext.dupe(); 194 195 className = PlasticInternalUtils.toClassName(classNode.name); 196 superClassName = PlasticInternalUtils.toClassName(classNode.superName); 197 int lastIndexOfDot = className.lastIndexOf('.'); 198 199 String packageName = lastIndexOfDot > -1 ? className.substring(0, lastIndexOfDot) : ""; 200 201 fieldInstrumentations = new FieldInstrumentations(classNode.superName); 202 203 annotationAccess = new DelegatingAnnotationAccess(pool.createAnnotationAccess(classNode.visibleAnnotations), 204 pool.createAnnotationAccess(superClassName)); 205 206 this.parentInheritanceData = parentInheritanceData; 207 208 inheritanceData = parentInheritanceData.createChild(packageName); 209 210 for (String interfaceName : classNode.interfaces) 211 { 212 inheritanceData.addInterface(interfaceName); 213 } 214 215 methods = new ArrayList(classNode.methods.size()); 216 217 String invalidConstructorMessage = invalidConstructorMessage(); 218 219 for (MethodNode node : classNode.methods) 220 { 221 if (node.name.equals(CONSTRUCTOR_NAME)) 222 { 223 if (node.desc.equals(NOTHING_TO_VOID)) 224 { 225 originalConstructor = node; 226 fieldTransformMethods.add(node); 227 } else 228 { 229 node.instructions.clear(); 230 231 newBuilder(node).throwException(IllegalStateException.class, invalidConstructorMessage); 232 } 233 234 continue; 235 } 236 237 /** 238 * Static methods are not visible to the main API methods, but they must still be transformed, 239 * in case they directly access fields. In addition, track their names to avoid collisions. 240 */ 241 if (Modifier.isStatic(node.access)) 242 { 243 if (isInheritableMethod(node)) 244 { 245 inheritanceData.addMethod(node.name, node.desc, node.access == 0); 246 } 247 248 methodNames.add(node.name); 249 250 fieldTransformMethods.add(node); 251 252 continue; 253 } 254 255 if (!Modifier.isAbstract(node.access)) 256 { 257 fieldTransformMethods.add(node); 258 } 259 260 PlasticMethodImpl pmi = new PlasticMethodImpl(this, node); 261 262 methods.add(pmi); 263 description2method.put(pmi.getDescription(), pmi); 264 265 if (isInheritableMethod(node)) 266 { 267 inheritanceData.addMethod(node.name, node.desc, node.access == 0); 268 } 269 270 methodNames.add(node.name); 271 } 272 273 methodNames.addAll(parentInheritanceData.methodNames()); 274 275 Collections.sort(methods); 276 277 fields = new ArrayList(classNode.fields.size()); 278 279 for (FieldNode node : classNode.fields) 280 { 281 fieldNames.add(node.name); 282 283 // Ignore static fields. 284 285 if (Modifier.isStatic(node.access)) 286 continue; 287 288 // When we instrument the field such that it must be private, we'll get an exception. 289 290 fields.add(new PlasticFieldImpl(this, node)); 291 } 292 293 Collections.sort(fields); 294 295 // TODO: Make the output class's constructor protected, and create a shim class to instantiate it 296 // efficiently (without reflection). 297 newConstructor = new MethodNode(ACC_PUBLIC, CONSTRUCTOR_NAME, CONSTRUCTOR_DESC, null, null); 298 constructorBuilder = newBuilder(newConstructor); 299 300 // Start by calling the super-class no args constructor 301 302 if (parentInheritanceData.isTransformed()) 303 { 304 // If the parent is transformed, our first step is always to invoke its constructor. 305 306 constructorBuilder.loadThis().loadArgument(0).loadArgument(1); 307 constructorBuilder.invokeConstructor(superClassName, StaticContext.class.getName(), 308 InstanceContext.class.getName()); 309 } else 310 { 311 // Assumes the base class includes a visible constructor that takes no arguments. 312 // TODO: Do a proper check for this case and throw a meaningful exception 313 // if not present. 314 315 constructorBuilder.loadThis().invokeConstructor(superClassName); 316 } 317 318 // During the transformation, we'll be adding code to the constructor to pull values 319 // out of the static or instance context and assign them to fields. 320 321 // Later on, we'll add the RETURN opcode 322 } 323 324 private String invalidConstructorMessage() 325 { 326 return String.format("Class %s has been transformed and may not be directly instantiated.", className); 327 } 328 329 @Override 330 public <T extends Annotation> boolean hasAnnotation(Class<T> annotationType) 331 { 332 check(); 333 334 return annotationAccess.hasAnnotation(annotationType); 335 } 336 337 @Override 338 public <T extends Annotation> T getAnnotation(Class<T> annotationType) 339 { 340 check(); 341 342 return annotationAccess.getAnnotation(annotationType); 343 } 344 345 private static void addMethodAndParameterAnnotationsFromExistingClass(MethodNode methodNode, MethodNode implementationMethodNode) 346 { 347 // visits the method attributes 348 int i, j, n; 349 if (implementationMethodNode.annotationDefault != null) 350 { 351 AnnotationVisitor av = methodNode.visitAnnotationDefault(); 352 AnnotationNode.accept(av, null, implementationMethodNode.annotationDefault); 353 if (av != null) 354 { 355 av.visitEnd(); 356 } 357 } 358 n = implementationMethodNode.visibleAnnotations == null ? 0 : implementationMethodNode.visibleAnnotations.size(); 359 for (i = 0; i < n; ++i) 360 { 361 AnnotationNode an = implementationMethodNode.visibleAnnotations.get(i); 362 an.accept(methodNode.visitAnnotation(an.desc, true)); 363 } 364 n = implementationMethodNode.invisibleAnnotations == null ? 0 : implementationMethodNode.invisibleAnnotations.size(); 365 for (i = 0; i < n; ++i) 366 { 367 AnnotationNode an = implementationMethodNode.invisibleAnnotations.get(i); 368 an.accept(methodNode.visitAnnotation(an.desc, false)); 369 } 370 n = implementationMethodNode.visibleParameterAnnotations == null 371 ? 0 372 : implementationMethodNode.visibleParameterAnnotations.length; 373 for (i = 0; i < n; ++i) 374 { 375 List<?> l = implementationMethodNode.visibleParameterAnnotations[i]; 376 if (l == null) 377 { 378 continue; 379 } 380 for (j = 0; j < l.size(); ++j) 381 { 382 AnnotationNode an = (AnnotationNode) l.get(j); 383 an.accept(methodNode.visitParameterAnnotation(i, an.desc, true)); 384 } 385 } 386 n = implementationMethodNode.invisibleParameterAnnotations == null 387 ? 0 388 : implementationMethodNode.invisibleParameterAnnotations.length; 389 for (i = 0; i < n; ++i) 390 { 391 List<?> l = implementationMethodNode.invisibleParameterAnnotations[i]; 392 if (l == null) 393 { 394 continue; 395 } 396 for (j = 0; j < l.size(); ++j) 397 { 398 AnnotationNode an = (AnnotationNode) l.get(j); 399 an.accept(methodNode.visitParameterAnnotation(i, an.desc, false)); 400 } 401 } 402 403 methodNode.visitEnd(); 404 405 } 406 407 private static void removeDuplicatedAnnotations(MethodNode node) 408 { 409 410 removeDuplicatedAnnotations(node.visibleAnnotations); 411 removeDuplicatedAnnotations(node.invisibleAnnotations); 412 413 if (node.visibleParameterAnnotations != null) 414 { 415 for (List<AnnotationNode> list : node.visibleParameterAnnotations) 416 { 417 removeDuplicatedAnnotations(list); 418 } 419 } 420 421 if (node.invisibleParameterAnnotations != null) 422 { 423 for (List<AnnotationNode> list : node.invisibleParameterAnnotations) 424 { 425 removeDuplicatedAnnotations(list); 426 } 427 } 428 429 } 430 431 private static void removeDuplicatedAnnotations(ClassNode node) 432 { 433 removeDuplicatedAnnotations(node.visibleAnnotations, true); 434 removeDuplicatedAnnotations(node.invisibleAnnotations, true); 435 } 436 437 private static void removeDuplicatedAnnotations(List<AnnotationNode> list) { 438 removeDuplicatedAnnotations(list, false); 439 } 440 441 private static void removeDuplicatedAnnotations(List<AnnotationNode> list, boolean reverse) { 442 443 if (list != null) 444 { 445 446 final Set<String> annotations = new HashSet<String>(); 447 final List<AnnotationNode> toBeRemoved = new ArrayList<AnnotationNode>(); 448 final List<AnnotationNode> toBeIterated; 449 450 if (reverse) 451 { 452 toBeIterated = new ArrayList<AnnotationNode>(list); 453 Collections.reverse(toBeIterated); 454 } 455 else { 456 toBeIterated = list; 457 } 458 459 for (AnnotationNode annotationNode : toBeIterated) 460 { 461 if (annotations.contains(annotationNode.desc)) 462 { 463 toBeRemoved.add(annotationNode); 464 } 465 else 466 { 467 annotations.add(annotationNode.desc); 468 } 469 } 470 471 for (AnnotationNode annotationNode : toBeRemoved) 472 { 473 list.remove(annotationNode); 474 } 475 476 } 477 478 } 479 480 private static String getParametersDesc(MethodNode methodNode) { 481 return methodNode.desc.substring(methodNode.desc.indexOf('(') + 1, methodNode.desc.lastIndexOf(')')); 482 } 483 484 private static MethodNode findExactMatchMethod(MethodNode methodNode, ClassNode source) { 485 486 MethodNode found = null; 487 488 final String methodDescription = getParametersDesc(methodNode); 489 490 for (MethodNode implementationMethodNode : source.methods) 491 { 492 493 final String implementationMethodDescription = getParametersDesc(implementationMethodNode); 494 if (methodNode.name.equals(implementationMethodNode.name) && 495 // We don't want synthetic methods. 496 ((implementationMethodNode.access & Opcodes.ACC_SYNTHETIC) == 0) 497 && (methodDescription.equals(implementationMethodDescription))) 498 { 499 found = implementationMethodNode; 500 break; 501 } 502 } 503 504 return found; 505 506 } 507 508 private static List<Class> getJavaParameterTypes(MethodNode methodNode) { 509 final ClassLoader classLoader = PlasticInternalUtils.class.getClassLoader(); 510 Type[] parameterTypes = Type.getArgumentTypes(methodNode.desc); 511 List<Class> list = new ArrayList<Class>(); 512 for (Type type : parameterTypes) 513 { 514 try 515 { 516 list.add(PlasticInternalUtils.toClass(classLoader, type.getClassName())); 517 } 518 catch (ClassNotFoundException e) 519 { 520 throw new RuntimeException(e); // shouldn't happen anyway 521 } 522 } 523 return list; 524 } 525 526 /** 527 * Returns the first method which matches the given methodNode. 528 * FIXME: this may not find the correct method if the correct one is declared after 529 * another in which all parameters are supertypes of the parameters of methodNode. 530 * To solve this, we would need to dig way deeper than we have time for this. 531 * @param methodNode 532 * @param classNode 533 * @return 534 */ 535 private static MethodNode findGenericMethod(MethodNode methodNode, ClassNode classNode) 536 { 537 538 MethodNode found = null; 539 540 List<Class> parameterTypes = getJavaParameterTypes(methodNode); 541 542 for (MethodNode implementationMethodNode : classNode.methods) 543 { 544 545 if (methodNode.name.equals(implementationMethodNode.name)) 546 { 547 548 final List<Class> implementationParameterTypes = getJavaParameterTypes(implementationMethodNode); 549 550 if (parameterTypes.size() == implementationParameterTypes.size()) 551 { 552 553 boolean matches = true; 554 for (int i = 0; i < parameterTypes.size(); i++) 555 { 556 final Class implementationParameterType = implementationParameterTypes.get(i); 557 final Class parameterType = parameterTypes.get(i); 558 if (!parameterType.isAssignableFrom(implementationParameterType)) { 559 matches = false; 560 break; 561 } 562 563 } 564 565 if (matches && !isBridge(implementationMethodNode)) 566 { 567 found = implementationMethodNode; 568 break; 569 } 570 571 } 572 573 } 574 575 } 576 577 return found; 578 579 } 580 581 private static void addMethodAndParameterAnnotationsFromExistingClass(MethodNode methodNode, ClassNode source) 582 { 583 if (source != null) 584 { 585 586 MethodNode candidate = findExactMatchMethod(methodNode, source); 587 588 final String parametersDesc = getParametersDesc(methodNode); 589 590 // candidate will be null when the method has generic parameters 591 if (candidate == null && parametersDesc.trim().length() > 0) 592 { 593 candidate = findGenericMethod(methodNode, source); 594 } 595 596 if (candidate != null) 597 { 598 addMethodAndParameterAnnotationsFromExistingClass(methodNode, candidate); 599 } 600 601 } 602 603 } 604 605 /** 606 * Tells whether a given method is a bridge one or not. 607 * Notice the flag for bridge method is the same as volatile field. Java 6 doesn't have 608 * Modifiers.isBridge(), so we use a workaround. 609 */ 610 private static boolean isBridge(MethodNode methodNode) 611 { 612 return Modifier.isVolatile(methodNode.access); 613 } 614 615 @Override 616 public PlasticClass proxyInterface(Class interfaceType, PlasticField field) 617 { 618 check(); 619 620 assert field != null; 621 622 introduceInterface(interfaceType); 623 624 // TAP5-2582: avoiding adding/delegating the same method more than once 625// for (Method m : interfaceType.getMethods()) 626// { 627// introduceMethod(m).delegateTo(field); 628// } 629 630 Map<MethodSignature, MethodDescription> map = createMethodSignatureMap(interfaceType); 631 for (MethodSignature methodSignature : map.keySet()) 632 { 633 introduceMethod(map.get(methodSignature)).delegateTo(field); 634 } 635 636 return this; 637 } 638 639 @Override 640 public PlasticClass proxyInterface(Class interfaceType, PlasticMethod method) 641 { 642 check(); 643 644 assert method != null; 645 646 introduceInterface(interfaceType); 647 648 // TAP5-2582: avoiding adding/delegating the same method more than once 649 Map<MethodSignature, MethodDescription> map = createMethodSignatureMap(interfaceType); 650 for (MethodSignature methodSignature : map.keySet()) 651 { 652 introduceMethod(map.get(methodSignature)).delegateTo(method); 653 } 654 655 return this; 656 } 657 658 @Override 659 public ClassInstantiator createInstantiator() 660 { 661 lock(); 662 663 addClassAnnotations(implementationClassNode); 664 removeDuplicatedAnnotations(classNode); 665 666 createShimIfNeeded(); 667 668 interceptFieldAccess(); 669 670 rewriteAdvisedMethods(); 671 672 completeConstructor(); 673 674 transformedClass = pool.realizeTransformedClass(classNode, inheritanceData, staticContext); 675 676 return createInstantiatorFromClass(transformedClass); 677 } 678 679 private void addClassAnnotations(ClassNode otherClassNode) 680 { 681 // Copy annotations from implementation if available. 682 // Code adapted from ClassNode.accept(), as we just want to copy 683 // the annotations and nothing more. 684 if (otherClassNode != null) 685 { 686 687 int i, n; 688 n = otherClassNode.visibleAnnotations == null ? 0 : otherClassNode.visibleAnnotations.size(); 689 for (i = 0; i < n; ++i) 690 { 691 AnnotationNode an = otherClassNode.visibleAnnotations.get(i); 692 an.accept(classNode.visitAnnotation(an.desc, true)); 693 } 694 n = otherClassNode.invisibleAnnotations == null ? 0 : otherClassNode.invisibleAnnotations.size(); 695 for (i = 0; i < n; ++i) 696 { 697 AnnotationNode an = otherClassNode.invisibleAnnotations.get(i); 698 an.accept(classNode.visitAnnotation(an.desc, false)); 699 } 700 701 } 702 } 703 704 private ClassInstantiator createInstantiatorFromClass(Class clazz) 705 { 706 try 707 { 708 Constructor ctor = clazz.getConstructor(StaticContext.class, InstanceContext.class); 709 710 return new ClassInstantiatorImpl(clazz, ctor, staticContext); 711 } catch (Exception ex) 712 { 713 throw new RuntimeException(String.format("Unable to create ClassInstantiator for class %s: %s", 714 clazz.getName(), PlasticInternalUtils.toMessage(ex)), ex); 715 } 716 } 717 718 private void completeConstructor() 719 { 720 if (originalConstructor != null) 721 { 722 convertOriginalConstructorToMethod(); 723 } 724 725 invokeCallbacks(); 726 727 constructorBuilder.returnResult(); 728 729 classNode.methods.add(newConstructor); 730 } 731 732 private void invokeCallbacks() 733 { 734 for (ConstructorCallback callback : constructorCallbacks) 735 { 736 invokeCallback(callback); 737 } 738 } 739 740 private void invokeCallback(ConstructorCallback callback) 741 { 742 int index = staticContext.store(callback); 743 744 // First, load the callback 745 746 constructorBuilder.loadArgument(0).loadConstant(index).invoke(STATIC_CONTEXT_GET_METHOD).castOrUnbox(ConstructorCallback.class.getName()); 747 748 // Load this and the InstanceContext 749 constructorBuilder.loadThis().loadArgument(1); 750 751 constructorBuilder.invoke(CONSTRUCTOR_CALLBACK_METHOD); 752 } 753 754 755 /** 756 * Convert the original constructor into a private method invoked from the 757 * generated constructor. 758 */ 759 private void convertOriginalConstructorToMethod() 760 { 761 String initializerName = makeUnique(methodNames, "initializeInstance"); 762 763 int originalAccess = originalConstructor.access; 764 765 originalConstructor.access = ACC_PRIVATE; 766 originalConstructor.name = initializerName; 767 768 stripOutSuperConstructorCall(originalConstructor); 769 770 constructorBuilder.loadThis().invokeVirtual(className, "void", initializerName); 771 772 // And replace it with a constructor that throws an exception 773 774 MethodNode replacementConstructor = new MethodNode(originalAccess, CONSTRUCTOR_NAME, NOTHING_TO_VOID, null, 775 null); 776 777 newBuilder(replacementConstructor).throwException(IllegalStateException.class, invalidConstructorMessage()); 778 779 classNode.methods.add(replacementConstructor); 780 } 781 782 private void stripOutSuperConstructorCall(MethodNode cons) 783 { 784 InsnList ins = cons.instructions; 785 786 ListIterator li = ins.iterator(); 787 788 // Look for the ALOAD 0 (i.e., push this on the stack) 789 while (li.hasNext()) 790 { 791 AbstractInsnNode node = (AbstractInsnNode) li.next(); 792 793 if (node.getOpcode() == ALOAD) 794 { 795 VarInsnNode varNode = (VarInsnNode) node; 796 797 assert varNode.var == 0; 798 799 // Remove the ALOAD 800 li.remove(); 801 break; 802 } 803 } 804 805 // Look for the call to the super-class, an INVOKESPECIAL 806 while (li.hasNext()) 807 { 808 AbstractInsnNode node = (AbstractInsnNode) li.next(); 809 810 if (node.getOpcode() == INVOKESPECIAL) 811 { 812 MethodInsnNode mnode = (MethodInsnNode) node; 813 814 assert mnode.owner.equals(classNode.superName); 815 assert mnode.name.equals(CONSTRUCTOR_NAME); 816 assert mnode.desc.equals(cons.desc); 817 818 li.remove(); 819 return; 820 } 821 } 822 823 throw new AssertionError("Could not convert constructor to simple method."); 824 } 825 826 @Override 827 public <T extends Annotation> List<PlasticField> getFieldsWithAnnotation(Class<T> annotationType) 828 { 829 check(); 830 831 List<PlasticField> result = getAllFields(); 832 833 Iterator<PlasticField> iterator = result.iterator(); 834 835 while (iterator.hasNext()) 836 { 837 PlasticField plasticField = iterator.next(); 838 839 if (!plasticField.hasAnnotation(annotationType)) 840 iterator.remove(); 841 } 842 843 return result; 844 } 845 846 @Override 847 public List<PlasticField> getAllFields() 848 { 849 check(); 850 851 return new ArrayList<PlasticField>(fields); 852 } 853 854 @Override 855 public List<PlasticField> getUnclaimedFields() 856 { 857 check(); 858 859 // Initially null, and set back to null by PlasticField.claim(). 860 861 if (unclaimedFields == null) 862 { 863 unclaimedFields = new ArrayList<PlasticField>(fields.size()); 864 865 for (PlasticField f : fields) 866 { 867 if (!f.isClaimed()) 868 unclaimedFields.add(f); 869 } 870 } 871 872 return unclaimedFields; 873 } 874 875 @Override 876 public PlasticMethod introducePrivateMethod(String typeName, String suggestedName, String[] argumentTypes, 877 String[] exceptionTypes) 878 { 879 check(); 880 881 assert PlasticInternalUtils.isNonBlank(typeName); 882 assert PlasticInternalUtils.isNonBlank(suggestedName); 883 884 String name = makeUnique(methodNames, suggestedName); 885 886 MethodDescription description = new MethodDescription(Modifier.PRIVATE, typeName, name, argumentTypes, null, 887 exceptionTypes); 888 889 return introduceMethod(description); 890 } 891 892 @Override 893 public PlasticField introduceField(String className, String suggestedName) 894 { 895 check(); 896 897 assert PlasticInternalUtils.isNonBlank(className); 898 assert PlasticInternalUtils.isNonBlank(suggestedName); 899 900 String name = makeUnique(fieldNames, suggestedName); 901 902 // No signature and no initial value 903 904 FieldNode fieldNode = new FieldNode(ACC_PRIVATE, name, PlasticInternalUtils.toDescriptor(className), null, null); 905 906 classNode.fields.add(fieldNode); 907 908 fieldNames.add(name); 909 910 PlasticFieldImpl newField = new PlasticFieldImpl(this, fieldNode); 911 912 return newField; 913 } 914 915 @Override 916 public PlasticField introduceField(Class fieldType, String suggestedName) 917 { 918 assert fieldType != null; 919 920 return introduceField(nameCache.toTypeName(fieldType), suggestedName); 921 } 922 923 String makeUnique(Set<String> values, String input) 924 { 925 return values.contains(input) ? input + "$" + PlasticUtils.nextUID() : input; 926 } 927 928 @Override 929 public <T extends Annotation> List<PlasticMethod> getMethodsWithAnnotation(Class<T> annotationType) 930 { 931 check(); 932 933 List<PlasticMethod> result = getMethods(); 934 Iterator<PlasticMethod> iterator = result.iterator(); 935 936 while (iterator.hasNext()) 937 { 938 PlasticMethod method = iterator.next(); 939 940 if (!method.hasAnnotation(annotationType)) 941 iterator.remove(); 942 } 943 944 return result; 945 } 946 947 @Override 948 public List<PlasticMethod> getMethods() 949 { 950 check(); 951 952 return new ArrayList<PlasticMethod>(methods); 953 } 954 955 @Override 956 public PlasticMethod introduceMethod(MethodDescription description) 957 { 958 check(); 959 960 if (Modifier.isAbstract(description.modifiers)) 961 { 962 description = description.withModifiers(description.modifiers & ~ACC_ABSTRACT); 963 } 964 965 PlasticMethod result = description2method.get(description); 966 967 if (result == null) 968 { 969 result = createNewMethod(description); 970 971 description2method.put(description, result); 972 } 973 974 methodNames.add(description.methodName); 975 976 // Note that is it not necessary to add the new MethodNode to 977 // fieldTransformMethods (the default implementations provided by introduceMethod() do not 978 // ever access instance fields) ... unless the caller invokes changeImplementation(). 979 980 return result; 981 } 982 983 @Override 984 public PlasticMethod introduceMethod(MethodDescription description, InstructionBuilderCallback callback) 985 { 986 check(); 987 988 // TODO: optimize this so that a default implementation is not created. 989 990 return introduceMethod(description).changeImplementation(callback); 991 } 992 993 @Override 994 public PlasticMethod introduceMethod(Method method) 995 { 996 check(); 997 998 return introduceMethod(new MethodDescription(method)); 999 } 1000 1001 void addMethod(MethodNode methodNode) 1002 { 1003 classNode.methods.add(methodNode); 1004 1005 methodNames.add(methodNode.name); 1006 1007 if (isInheritableMethod(methodNode)) 1008 { 1009 inheritanceData.addMethod(methodNode.name, methodNode.desc, methodNode.access == 0); 1010 } 1011 } 1012 1013 private PlasticMethod createNewMethod(MethodDescription description) 1014 { 1015 if (Modifier.isStatic(description.modifiers)) 1016 throw new IllegalArgumentException(String.format( 1017 "Unable to introduce method '%s' into class %s: introduced methods may not be static.", 1018 description, className)); 1019 1020 String desc = nameCache.toDesc(description); 1021 1022 String[] exceptions = new String[description.checkedExceptionTypes.length]; 1023 for (int i = 0; i < exceptions.length; i++) 1024 { 1025 exceptions[i] = PlasticInternalUtils.toInternalName(description.checkedExceptionTypes[i]); 1026 } 1027 1028 MethodNode methodNode = new MethodNode(description.modifiers, description.methodName, desc, 1029 description.genericSignature, exceptions); 1030 boolean isOverride = inheritanceData.isImplemented(methodNode.name, desc); 1031 1032 if (!isOverride) 1033 { 1034 addMethodAndParameterAnnotationsFromExistingClass(methodNode, implementationClassNode); 1035 addMethodAndParameterAnnotationsFromExistingClass(methodNode, interfaceClassNode); 1036 removeDuplicatedAnnotations(methodNode); 1037 } 1038 1039 if (isOverride) 1040 createOverrideOfBaseClassImpl(description, methodNode); 1041 else 1042 createNewMethodImpl(description, methodNode); 1043 1044 addMethod(methodNode); 1045 1046 return new PlasticMethodImpl(this, methodNode); 1047 } 1048 1049 private boolean isDefaultMethod(Method method) 1050 { 1051 return method.getDeclaringClass().isInterface() && 1052 (method.getModifiers() & (Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_ABSTRACT)) == Opcodes.ACC_PUBLIC; 1053 } 1054 1055 private void createNewMethodImpl(MethodDescription methodDescription, MethodNode methodNode) 1056 { 1057 newBuilder(methodDescription, methodNode).returnDefaultValue(); 1058 } 1059 1060 private void createOverrideOfBaseClassImpl(MethodDescription methodDescription, MethodNode methodNode) 1061 { 1062 InstructionBuilder builder = newBuilder(methodDescription, methodNode); 1063 1064 builder.loadThis(); 1065 builder.loadArguments(); 1066 builder.invokeSpecial(superClassName, methodDescription); 1067 builder.returnResult(); 1068 } 1069 1070 /** 1071 * Iterates over all non-introduced methods, including the original constructor. For each 1072 * method, the bytecode is scanned for field reads and writes. When a match is found against an intercepted field, 1073 * the operation is replaced with a method invocation. This is invoked only after the {@link PlasticClassHandleShim} 1074 * for the class has been created, as the shim may create methods that contain references to fields that may be 1075 * subject to field access interception. 1076 */ 1077 private void interceptFieldAccess() 1078 { 1079 for (MethodNode node : fieldTransformMethods) 1080 { 1081 // Intercept field access inside the method, tracking which access methods 1082 // are actually used by removing them from accessMethods 1083 1084 interceptFieldAccess(node); 1085 } 1086 } 1087 1088 /** 1089 * Determines if any fields or methods have provided FieldHandles or MethodHandles; if so 1090 * a shim class must be created to facilitate read/write access to fields, or invocation of methods. 1091 */ 1092 private void createShimIfNeeded() 1093 { 1094 if (shimFields.isEmpty() && shimMethods.isEmpty()) 1095 return; 1096 1097 PlasticClassHandleShim shim = createShimInstance(); 1098 1099 installShim(shim); 1100 } 1101 1102 public void installShim(PlasticClassHandleShim shim) 1103 { 1104 for (PlasticFieldImpl f : shimFields) 1105 { 1106 f.installShim(shim); 1107 } 1108 1109 for (PlasticMethodImpl m : shimMethods) 1110 { 1111 m.installShim(shim); 1112 } 1113 } 1114 1115 public PlasticClassHandleShim createShimInstance() 1116 { 1117 String shimClassName = String.format("%s$Shim_%s", classNode.name, PlasticUtils.nextUID()); 1118 1119 ClassNode shimClassNode = new ClassNode(); 1120 1121 shimClassNode.visit(PlasticConstants.DEFAULT_VERSION_OPCODE, ACC_PUBLIC | ACC_FINAL, shimClassName, null, HANDLE_SHIM_BASE_CLASS_INTERNAL_NAME, 1122 null); 1123 1124 implementConstructor(shimClassNode); 1125 1126 if (!shimFields.isEmpty()) 1127 { 1128 implementShimGet(shimClassNode); 1129 implementShimSet(shimClassNode); 1130 } 1131 1132 if (!shimMethods.isEmpty()) 1133 { 1134 implementShimInvoke(shimClassNode); 1135 } 1136 1137 return instantiateShim(shimClassNode); 1138 } 1139 1140 private void implementConstructor(ClassNode shimClassNode) 1141 { 1142 MethodNode mn = new MethodNode(ACC_PUBLIC, CONSTRUCTOR_NAME, NOTHING_TO_VOID, null, null); 1143 1144 InstructionBuilder builder = newBuilder(mn); 1145 1146 builder.loadThis().invokeConstructor(PlasticClassHandleShim.class).returnResult(); 1147 1148 shimClassNode.methods.add(mn); 1149 1150 } 1151 1152 private PlasticClassHandleShim instantiateShim(ClassNode shimClassNode) 1153 { 1154 try 1155 { 1156 Class shimClass = pool.realize(className, ClassType.SUPPORT, shimClassNode); 1157 1158 return (PlasticClassHandleShim) shimClass.newInstance(); 1159 } catch (Exception ex) 1160 { 1161 throw new RuntimeException( 1162 String.format("Unable to instantiate shim class %s for plastic class %s: %s", 1163 PlasticInternalUtils.toClassName(shimClassNode.name), className, 1164 PlasticInternalUtils.toMessage(ex)), ex); 1165 } 1166 } 1167 1168 private void implementShimGet(ClassNode shimClassNode) 1169 { 1170 MethodNode mn = new MethodNode(ACC_PUBLIC, "get", OBJECT_INT_TO_OBJECT, null, null); 1171 1172 InstructionBuilder builder = newBuilder(mn); 1173 1174 // Arg 0 is the target instance 1175 // Arg 1 is the index 1176 1177 builder.loadArgument(0).checkcast(className); 1178 builder.loadArgument(1); 1179 1180 builder.startSwitch(0, nextFieldIndex - 1, new SwitchCallback() 1181 { 1182 @Override 1183 public void doSwitch(SwitchBlock block) 1184 { 1185 for (PlasticFieldImpl f : shimFields) 1186 { 1187 f.extendShimGet(block); 1188 } 1189 } 1190 }); 1191 1192 shimClassNode.methods.add(mn); 1193 } 1194 1195 private void implementShimSet(ClassNode shimClassNode) 1196 { 1197 MethodNode mn = new MethodNode(ACC_PUBLIC, "set", OBJECT_INT_OBJECT_TO_VOID, null, null); 1198 1199 InstructionBuilder builder = newBuilder(mn); 1200 1201 // Arg 0 is the target instance 1202 // Arg 1 is the index 1203 // Arg 2 is the new value 1204 1205 builder.loadArgument(0).checkcast(className); 1206 builder.loadArgument(2); 1207 1208 builder.loadArgument(1); 1209 1210 builder.startSwitch(0, nextFieldIndex - 1, new SwitchCallback() 1211 { 1212 @Override 1213 public void doSwitch(SwitchBlock block) 1214 { 1215 for (PlasticFieldImpl f : shimFields) 1216 { 1217 f.extendShimSet(block); 1218 } 1219 } 1220 }); 1221 1222 builder.returnResult(); 1223 1224 shimClassNode.methods.add(mn); 1225 } 1226 1227 private void implementShimInvoke(ClassNode shimClassNode) 1228 { 1229 MethodNode mn = new MethodNode(ACC_PUBLIC, "invoke", OBJECT_INT_OBJECT_ARRAY_TO_METHOD_INVOCATION_RESULT, null, 1230 null); 1231 1232 InstructionBuilder builder = newBuilder(mn); 1233 1234 // Arg 0 is the target instance 1235 // Arg 1 is the index 1236 // Arg 2 is the object array of parameters 1237 1238 builder.loadArgument(0).checkcast(className); 1239 1240 builder.loadArgument(1); 1241 1242 builder.startSwitch(0, nextMethodIndex - 1, new SwitchCallback() 1243 { 1244 @Override 1245 public void doSwitch(SwitchBlock block) 1246 { 1247 for (PlasticMethodImpl m : shimMethods) 1248 { 1249 m.extendShimInvoke(block); 1250 } 1251 } 1252 }); 1253 1254 shimClassNode.methods.add(mn); 1255 } 1256 1257 private void rewriteAdvisedMethods() 1258 { 1259 for (PlasticMethodImpl method : advisedMethods) 1260 { 1261 method.rewriteMethodForAdvice(); 1262 } 1263 } 1264 1265 private void interceptFieldAccess(MethodNode methodNode) 1266 { 1267 InsnList insns = methodNode.instructions; 1268 1269 ListIterator it = insns.iterator(); 1270 1271 while (it.hasNext()) 1272 { 1273 AbstractInsnNode node = (AbstractInsnNode) it.next(); 1274 1275 int opcode = node.getOpcode(); 1276 1277 if (opcode != GETFIELD && opcode != PUTFIELD) 1278 { 1279 continue; 1280 } 1281 1282 FieldInsnNode fnode = (FieldInsnNode) node; 1283 1284 FieldInstrumentation instrumentation = findFieldNodeInstrumentation(fnode, opcode == GETFIELD); 1285 1286 if (instrumentation == null) 1287 { 1288 continue; 1289 } 1290 1291 // Replace the field access node with the appropriate method invocation. 1292 1293 insns.insertBefore(fnode, new MethodInsnNode(INVOKEVIRTUAL, fnode.owner, instrumentation.methodName, instrumentation.methodDescription)); 1294 1295 it.remove(); 1296 } 1297 } 1298 1299 private FieldInstrumentation findFieldNodeInstrumentation(FieldInsnNode node, boolean forRead) 1300 { 1301 // First look in the local fieldInstrumentations, which contains private field instrumentations 1302 // (as well as non-private ones). 1303 1304 String searchStart = node.owner; 1305 1306 if (searchStart.equals(classNode.name)) 1307 { 1308 FieldInstrumentation result = fieldInstrumentations.get(node.name, forRead); 1309 1310 if (result != null) 1311 { 1312 return result; 1313 } 1314 1315 // Slight optimization: start the search in the super-classes' fields, since we've already 1316 // checked this classes fields. 1317 1318 searchStart = classNode.superName; 1319 } 1320 1321 return pool.getFieldInstrumentation(searchStart, node.name, forRead); 1322 } 1323 1324 String getInstanceContextFieldName() 1325 { 1326 if (instanceContextFieldName == null) 1327 { 1328 instanceContextFieldName = makeUnique(fieldNames, "instanceContext"); 1329 1330 // TODO: We could use a protected field and only initialize 1331 // it once, in the first base class where it is needed, though that raises the possibilities 1332 // of name conflicts (a subclass might introduce a field with a conflicting name). 1333 1334 FieldNode node = new FieldNode(ACC_PRIVATE | ACC_FINAL, instanceContextFieldName, INSTANCE_CONTEXT_DESC, 1335 null, null); 1336 1337 classNode.fields.add(node); 1338 1339 // Extend the constructor to store the context in a field. 1340 1341 constructorBuilder.loadThis().loadArgument(1) 1342 .putField(className, instanceContextFieldName, InstanceContext.class); 1343 } 1344 1345 return instanceContextFieldName; 1346 } 1347 1348 /** 1349 * Creates a new private final field and initializes its value (using the StaticContext). 1350 */ 1351 String createAndInitializeFieldFromStaticContext(String suggestedFieldName, String fieldType, 1352 Object injectedFieldValue) 1353 { 1354 String name = makeUnique(fieldNames, suggestedFieldName); 1355 1356 FieldNode field = new FieldNode(ACC_PRIVATE | ACC_FINAL, name, nameCache.toDesc(fieldType), null, null); 1357 1358 classNode.fields.add(field); 1359 1360 initializeFieldFromStaticContext(name, fieldType, injectedFieldValue); 1361 1362 return name; 1363 } 1364 1365 /** 1366 * Initializes a field from the static context. The injected value is added to the static 1367 * context and the class constructor updated to assign the value from the context (which includes casting and 1368 * possibly unboxing). 1369 */ 1370 void initializeFieldFromStaticContext(String fieldName, String fieldType, Object injectedFieldValue) 1371 { 1372 int index = staticContext.store(injectedFieldValue); 1373 1374 // Although it feels nicer to do the loadThis() later and then swap(), that breaks 1375 // on primitive longs and doubles, so its just easier to do the loadThis() first 1376 // so its at the right place on the stack for the putField(). 1377 1378 constructorBuilder.loadThis(); 1379 1380 constructorBuilder.loadArgument(0).loadConstant(index); 1381 constructorBuilder.invoke(STATIC_CONTEXT_GET_METHOD); 1382 constructorBuilder.castOrUnbox(fieldType); 1383 1384 constructorBuilder.putField(className, fieldName, fieldType); 1385 } 1386 1387 void pushInstanceContextFieldOntoStack(InstructionBuilder builder) 1388 { 1389 builder.loadThis().getField(className, getInstanceContextFieldName(), InstanceContext.class); 1390 } 1391 1392 @Override 1393 public PlasticClass getPlasticClass() 1394 { 1395 return this; 1396 } 1397 1398 @Override 1399 public Class<?> getTransformedClass() 1400 { 1401 if (transformedClass == null) 1402 throw new IllegalStateException(String.format( 1403 "Transformed class %s is not yet available because the transformation is not yet complete.", 1404 className)); 1405 1406 return transformedClass; 1407 } 1408 1409 private boolean isInheritableMethod(MethodNode node) 1410 { 1411 return !Modifier.isPrivate(node.access); 1412 } 1413 1414 @Override 1415 public String getClassName() 1416 { 1417 return className; 1418 } 1419 1420 InstructionBuilderImpl newBuilder(MethodNode mn) 1421 { 1422 return newBuilder(PlasticInternalUtils.toMethodDescription(mn), mn); 1423 } 1424 1425 InstructionBuilderImpl newBuilder(MethodDescription description, MethodNode mn) 1426 { 1427 return new InstructionBuilderImpl(description, mn, nameCache); 1428 } 1429 1430 @Override 1431 public Set<PlasticMethod> introduceInterface(Class interfaceType) 1432 { 1433 check(); 1434 1435 assert interfaceType != null; 1436 1437 if (!interfaceType.isInterface()) 1438 throw new IllegalArgumentException(String.format( 1439 "Class %s is not an interface; only interfaces may be introduced.", interfaceType.getName())); 1440 1441 String interfaceName = nameCache.toInternalName(interfaceType); 1442 1443 try 1444 { 1445 interfaceClassNode = PlasticClassPool.readClassNode(interfaceType.getName(), getClass().getClassLoader()); 1446 } catch (IOException e) 1447 { 1448 throw new RuntimeException(e); 1449 } 1450 1451 if (!inheritanceData.isInterfaceImplemented(interfaceName)) 1452 { 1453 classNode.interfaces.add(interfaceName); 1454 inheritanceData.addInterface(interfaceName); 1455 } 1456 1457 addClassAnnotations(interfaceClassNode); 1458 1459 Set<PlasticMethod> introducedMethods = new HashSet<PlasticMethod>(); 1460 1461 Map<MethodSignature, MethodDescription> map = createMethodSignatureMap(interfaceType); 1462 1463 // for (Method m : interfaceType.getMethods()) 1464 for (MethodSignature methodSignature : map.keySet()) 1465 { 1466 // MethodDescription description = new MethodDescription(m); 1467 final MethodDescription description = map.get(methodSignature); 1468 1469 if (!isMethodImplemented(description) && !isDefaultMethod(methodSignature.method)) 1470 { 1471 // introducedMethods.add(introduceMethod(m)); 1472 introducedMethods.add(introduceMethod(description)); 1473 } 1474 } 1475 1476 interfaceClassNode = null; 1477 1478 return introducedMethods; 1479 } 1480 1481 private Map<MethodSignature, MethodDescription> createMethodSignatureMap(Class interfaceType) { 1482 // TAP-2582: preprocessing the method list so we don't add duplicated 1483 // methods, something that happens when an interface has superinterfaces 1484 // and they define the same method signature. 1485 // In addition, we collect all the thrown checked exceptions, just in case. 1486 Map<MethodSignature, MethodDescription> map = new HashMap<MethodSignature, MethodDescription>(); 1487 for (Method m : interfaceType.getMethods()) 1488 { 1489 final MethodSignature methodSignature = new MethodSignature(m); 1490 final MethodDescription newMethodDescription = new MethodDescription(m); 1491 if (!map.containsKey(methodSignature)) 1492 { 1493 map.put(methodSignature, newMethodDescription); 1494 } 1495 else 1496 { 1497 if (newMethodDescription.checkedExceptionTypes != null && newMethodDescription.checkedExceptionTypes.length > 0) 1498 { 1499 final MethodDescription methodDescription = map.get(methodSignature); 1500 final Set<String> checkedExceptionTypes = new HashSet<String>(); 1501 checkedExceptionTypes.addAll(Arrays.asList(methodDescription.checkedExceptionTypes)); 1502 checkedExceptionTypes.addAll(Arrays.asList(newMethodDescription.checkedExceptionTypes)); 1503 map.put(methodSignature, new MethodDescription( 1504 methodDescription, 1505 checkedExceptionTypes.toArray(new String[checkedExceptionTypes.size()]))); 1506 } 1507 } 1508 } 1509 return map; 1510 } 1511 1512 final private static class MethodSignature implements Comparable<MethodSignature>{ 1513 1514 final private Method method; 1515 final private String name; 1516 final private Class<?>[] parameterTypes; 1517 1518 public MethodSignature(Method method) { 1519 this.method = method; 1520 this.name = method.getName(); 1521 this.parameterTypes = method.getParameterTypes(); 1522 } 1523 1524 @Override 1525 public int hashCode() { 1526 final int prime = 31; 1527 int result = 1; 1528 result = prime * result + Arrays.hashCode(parameterTypes); 1529 result = prime * result + ((name == null) ? 0 : name.hashCode()); 1530 return result; 1531 } 1532 1533 @Override 1534 public boolean equals(Object obj) { 1535 if (this == obj) 1536 return true; 1537 if (obj == null) 1538 return false; 1539 if (getClass() != obj.getClass()) 1540 return false; 1541 MethodSignature other = (MethodSignature) obj; 1542 if (!Arrays.equals(parameterTypes, other.parameterTypes)) 1543 return false; 1544 if (name == null) { 1545 if (other.name != null) 1546 return false; 1547 } else if (!name.equals(other.name)) 1548 return false; 1549 return true; 1550 } 1551 1552 @Override 1553 public int compareTo(MethodSignature o) { 1554 return method.getName().compareTo(o.method.getName()); 1555 } 1556 } 1557 1558 @Override 1559 public PlasticClass addToString(final String toStringValue) 1560 { 1561 check(); 1562 1563 if (!isMethodImplemented(PlasticUtils.TO_STRING_DESCRIPTION)) 1564 { 1565 introduceMethod(PlasticUtils.TO_STRING_DESCRIPTION, new InstructionBuilderCallback() 1566 { 1567 @Override 1568 public void doBuild(InstructionBuilder builder) 1569 { 1570 builder.loadConstant(toStringValue).returnResult(); 1571 } 1572 }); 1573 } 1574 1575 return this; 1576 } 1577 1578 @Override 1579 public boolean isMethodImplemented(MethodDescription description) 1580 { 1581 return inheritanceData.isImplemented(description.methodName, nameCache.toDesc(description)); 1582 } 1583 1584 @Override 1585 public boolean isInterfaceImplemented(Class interfaceType) 1586 { 1587 assert interfaceType != null; 1588 assert interfaceType.isInterface(); 1589 1590 String interfaceName = nameCache.toInternalName(interfaceType); 1591 1592 return inheritanceData.isInterfaceImplemented(interfaceName); 1593 } 1594 1595 @Override 1596 public String getSuperClassName() 1597 { 1598 return superClassName; 1599 } 1600 1601 @Override 1602 public PlasticClass onConstruct(ConstructorCallback callback) 1603 { 1604 check(); 1605 1606 assert callback != null; 1607 1608 constructorCallbacks.add(callback); 1609 1610 return this; 1611 } 1612 1613 void redirectFieldWrite(String fieldName, boolean privateField, MethodNode method) 1614 { 1615 FieldInstrumentation fi = new FieldInstrumentation(method.name, method.desc); 1616 1617 fieldInstrumentations.write.put(fieldName, fi); 1618 1619 if (!(proxy || privateField)) 1620 { 1621 pool.setFieldWriteInstrumentation(classNode.name, fieldName, fi); 1622 } 1623 } 1624 1625 void redirectFieldRead(String fieldName, boolean privateField, MethodNode method) 1626 { 1627 FieldInstrumentation fi = new FieldInstrumentation(method.name, method.desc); 1628 1629 fieldInstrumentations.read.put(fieldName, fi); 1630 1631 if (!(proxy || privateField)) 1632 { 1633 pool.setFieldReadInstrumentation(classNode.name, fieldName, fi); 1634 } 1635 } 1636 1637 @Override 1638 public String toString() 1639 { 1640 return String.format("PlasticClassImpl[%s]", className); 1641 } 1642}