001 package net.sourceforge.retroweaver; 002 003 import java.io.ByteArrayInputStream; 004 import java.io.ByteArrayOutputStream; 005 import java.io.File; 006 import java.io.FileFilter; 007 import java.io.FileInputStream; 008 import java.io.FileOutputStream; 009 import java.io.IOException; 010 import java.io.InputStream; 011 import java.io.OutputStream; 012 import java.util.ArrayList; 013 import java.util.Collections; 014 import java.util.HashSet; 015 import java.util.List; 016 import java.util.Set; 017 import java.util.TreeSet; 018 import java.util.jar.JarEntry; 019 import java.util.jar.JarFile; 020 import java.util.jar.JarOutputStream; 021 022 import net.sourceforge.retroweaver.event.WeaveListener; 023 import net.sourceforge.retroweaver.optimizer.ClassConstantsCollector; 024 import net.sourceforge.retroweaver.optimizer.Constant; 025 import net.sourceforge.retroweaver.optimizer.ConstantComparator; 026 import net.sourceforge.retroweaver.optimizer.ConstantPool; 027 import net.sourceforge.retroweaver.translator.NameSpace; 028 import net.sourceforge.retroweaver.translator.NameTranslator; 029 import net.sourceforge.retroweaver.translator.NameTranslatorClassVisitor; 030 import net.sourceforge.retroweaver.translator.TranslatorException; 031 032 import org.objectweb.asm.Attribute; 033 import org.objectweb.asm.ClassAdapter; 034 import org.objectweb.asm.ClassReader; 035 import org.objectweb.asm.ClassVisitor; 036 import org.objectweb.asm.ClassWriter; 037 import org.objectweb.asm.FieldVisitor; 038 import org.objectweb.asm.Label; 039 import org.objectweb.asm.MethodAdapter; 040 import org.objectweb.asm.MethodVisitor; 041 import org.objectweb.asm.Opcodes; 042 import org.objectweb.asm.Type; 043 044 /** 045 * A bytecode enhancer that translates Java 1.5 class files into Java 1.4 class 046 * files. The enhancer performs primarily two tasks: 1) Reverses changes made to 047 * the class file format in 1.5 to the former 1.4 format. 2) Replaces compiler 048 * generated calls into the new 1.5 runtime with calls into RetroWeaver's 049 * replacement runtime. 050 */ 051 public class RetroWeaver { 052 053 private final int target; 054 055 private boolean lazy; 056 057 /** 058 * Indicates whether the generic signatures should be stripped. Default to <code>false</code>. 059 */ 060 private boolean stripSignatures; 061 062 /** 063 * Indicates whether the custom retroweaver attributes should be stripped. Default to <code>false</code>. 064 */ 065 private boolean stripAttributes; 066 067 private int weavedClassCount; 068 069 private WeaveListener listener; 070 071 private RefVerifier verifier; 072 073 private static final String newLine = System.getProperty("line.separator"); 074 075 public RetroWeaver(int target) { 076 this.target = target; 077 } 078 079 protected static final FileFilter classFilter = new FileFilter() { 080 public boolean accept(File f) { 081 return f.getName().endsWith(".class"); 082 } 083 }; 084 085 protected static final FileFilter subdirFilter = new FileFilter() { 086 public boolean accept(File f) { 087 return f.isDirectory(); 088 } 089 }; 090 091 protected static void buildFileSets(ArrayList<File[]> fileSets, File path) { 092 File[] files = path.listFiles(classFilter); 093 if (files != null) { 094 fileSets.add(files); 095 } 096 097 File[] subdirs = path.listFiles(subdirFilter); 098 if (subdirs != null) { 099 for (File subdir : subdirs) { 100 buildFileSets(fileSets, subdir); 101 } 102 } 103 } 104 105 private void displayStartMessage(int n) { 106 if (n > 0) { 107 listener.weavingStarted("Processing " + n + (n == 1?" class":" classes")); 108 } 109 } 110 111 private void displayEndMessage() { 112 if (weavedClassCount > 0) { 113 listener.weavingCompleted(Integer.toString(weavedClassCount) + (weavedClassCount == 1?" class":" classes") + " weaved."); 114 } 115 } 116 117 public void weave(File path) throws IOException { 118 ArrayList<File[]> fileSets = new ArrayList<File[]>(); 119 120 buildFileSets(fileSets, path); 121 122 int n = 0; 123 for (File[] set : fileSets) { 124 n += set.length; 125 } 126 displayStartMessage(n); 127 128 for (int i = 0; i < fileSets.size(); i++) { 129 for (File file : fileSets.get(i)) { 130 String sourcePath = file.getCanonicalPath(); 131 weave(sourcePath, null); 132 } 133 } 134 displayEndMessage(); 135 136 if (verifier != null) { 137 verifier.verifyFiles(); 138 verifier.displaySummary(); 139 } 140 } 141 142 public void weave(File[] baseDirs, String[][] fileSets, File outputDir) 143 throws IOException { 144 int n = 0; 145 for (String[] set : fileSets) { 146 n += set.length; 147 } 148 displayStartMessage(n); 149 150 Set<String> weaved = new HashSet<String>(); 151 for (int i = 0; i < fileSets.length; i++) { 152 for (String fileName : fileSets[i]) { 153 File file = new File(baseDirs[i], fileName); 154 String sourcePath = file.getCanonicalPath(); 155 String outputPath = null; 156 if (outputDir != null) { 157 outputPath = new File(outputDir, fileName) 158 .getCanonicalPath(); 159 } 160 // Weave it unless already weaved. 161 if (!weaved.contains(sourcePath)) { 162 weave(sourcePath, outputPath); 163 weaved.add(sourcePath); 164 } 165 } 166 } 167 displayEndMessage(); 168 169 if (verifier != null) { 170 verifier.verifyFiles(); 171 verifier.displaySummary(); 172 } 173 } 174 175 public void weaveJarFile(String sourceJarFileName, String destJarFileName) 176 throws IOException { 177 JarFile jarFile = new JarFile(sourceJarFileName); 178 ArrayList<JarEntry> entries = Collections.list(jarFile.entries()); 179 180 OutputStream os = new FileOutputStream(destJarFileName); 181 JarOutputStream out = new JarOutputStream(os); 182 183 int n = 0; 184 for (JarEntry entry : entries) { 185 if (entry.getName().endsWith(".class")) { 186 n++; 187 } 188 } 189 displayStartMessage(n); 190 191 for (JarEntry entry : entries) { 192 String name = entry.getName(); 193 InputStream dataStream = null; 194 if (name.endsWith(".class")) { 195 // weave class 196 InputStream is = jarFile.getInputStream(entry); 197 ByteArrayOutputStream classStream = new ByteArrayOutputStream(); 198 if (weave(is, name, classStream)) { 199 // class file was modified 200 weavedClassCount++; 201 202 dataStream = new ByteArrayInputStream(classStream 203 .toByteArray()); 204 205 // create new entry 206 entry = new JarEntry(name); 207 recordFileForVerifier(name); 208 } 209 } 210 211 if (dataStream == null) { 212 // not a class file or class wasn't no 213 dataStream = jarFile.getInputStream(entry); 214 } 215 // writing entry 216 out.putNextEntry(new JarEntry(name)); 217 218 // writing data 219 int len; 220 final byte[] buf = new byte[1024]; 221 while ((len = dataStream.read(buf)) >= 0) { 222 out.write(buf, 0, len); 223 } 224 } 225 out.close(); 226 227 displayEndMessage(); 228 229 if (verifier != null) { 230 verifier.verifyJarFile(destJarFileName); 231 verifier.displaySummary(); 232 } 233 } 234 235 public void weave(String sourcePath, String outputPath) throws IOException { 236 InputStream is = new FileInputStream(sourcePath); 237 try { 238 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 239 if (weave(is, sourcePath, bos)) { 240 // new class was generated 241 weavedClassCount++; 242 243 String path; 244 245 if (outputPath == null) { 246 path = sourcePath; 247 } else { 248 path = outputPath; 249 // create parent dir if necessary 250 File parentDir = new File(path).getParentFile(); 251 if (parentDir != null) { 252 parentDir.mkdirs(); 253 } 254 } 255 FileOutputStream fos = new FileOutputStream(path); 256 fos.write(bos.toByteArray()); 257 fos.close(); 258 259 recordFileForVerifier(path); 260 } else { 261 // We're lazy and the class already has the target version. 262 263 if (outputPath == null) { 264 // weaving in place 265 return; 266 } 267 268 File dir = new File(outputPath).getParentFile(); 269 if (dir != null) { 270 dir.mkdirs(); 271 } 272 273 File sf = new File(sourcePath); 274 File of = new File(outputPath); 275 276 if (!of.isFile() 277 || !of.getCanonicalPath().equals(sf.getCanonicalPath())) { 278 // Target doesn't exist or is different from source so copy 279 // the file and transfer utime. 280 FileInputStream fis = new FileInputStream(sf); 281 byte[] bytes = new byte[(int) sf.length()]; 282 fis.read(bytes); 283 fis.close(); 284 FileOutputStream fos = new FileOutputStream(of); 285 fos.write(bytes); 286 fos.close(); 287 of.setLastModified(sf.lastModified()); 288 } 289 } 290 } finally { 291 try { 292 is.close(); 293 } catch (IOException e) { // NOPMD by xlv 294 } 295 } 296 } 297 298 private void recordFileForVerifier(String fileName) { 299 if (verifier != null) { 300 verifier.addClass(fileName); 301 } 302 } 303 304 private static final boolean COMPACT_CONSTANTS = true; 305 306 protected static final Attribute[] CUSTOM_ATTRIBUTES = { 307 new RetroWeaverAttribute(Weaver.getBuildNumber(), Weaver.VERSION_1_5) 308 }; 309 310 private boolean classpathChecked; 311 312 private boolean isRuntimeInClassPath() { 313 if (!classpathChecked) { 314 try { 315 Class.forName("net.sourceforge.retroweaver.runtime.java.lang.annotation.AIB"); 316 classpathChecked = true; 317 } catch (ClassNotFoundException e) { 318 listener.weavingError("Error: the retroweaver runtime must be in the classpath"); 319 return false; 320 } 321 } 322 return true; 323 } 324 325 protected boolean weave(InputStream sourceStream, String fileName, ByteArrayOutputStream bos) 326 throws IOException { 327 328 if (!isRuntimeInClassPath()) { 329 return false; 330 } 331 332 ClassReader cr = new ClassReader(sourceStream); 333 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); 334 335 try { 336 // chain class visitors 337 ClassVisitor classVisitor = cw; 338 ConstantPool cp; 339 if (COMPACT_CONSTANTS) { 340 cp = new ConstantPool(); 341 classVisitor = new ClassConstantsCollector(classVisitor, cp); 342 } 343 classVisitor = new NameTranslatorClassVisitor(classVisitor, NameTranslator.getGeneralTranslator()); 344 classVisitor = new ClassWeaver(classVisitor, 345 lazy, stripAttributes, target, listener); 346 347 // StringBuilder translation will be done before general weaving and 348 // mirror translation: trimToSize() calls will be processed correctly 349 // and no need to do translations in general weaving process 350 classVisitor = new NameTranslatorClassVisitor(classVisitor, NameTranslator.getStringBuilderTranslator()); 351 352 if (stripSignatures) { 353 classVisitor = new SignatureStripper(classVisitor); 354 } 355 356 cr.accept(classVisitor, CUSTOM_ATTRIBUTES, ClassReader.EXPAND_FRAMES); 357 358 if (COMPACT_CONSTANTS) { 359 Set<Constant> constants = new TreeSet<Constant>(new ConstantComparator()); 360 constants.addAll(cp.values()); 361 362 cr = new ClassReader(cw.toByteArray()); 363 cw = new ClassWriter(0); 364 for(Constant c: constants) { 365 c.write(cw); 366 } 367 cr.accept(cw, 0); 368 } 369 370 bos.write(cw.toByteArray()); 371 return true; 372 } catch (TranslatorException te) { 373 listener.weavingError(te.getMessage()); 374 return false; 375 } catch (LazyException e) { 376 return false; 377 } 378 } 379 380 public void setListener(WeaveListener listener) { 381 this.listener = listener; 382 } 383 384 public void setLazy(boolean lazy) { 385 this.lazy = lazy; 386 } 387 388 public void setVerifier(RefVerifier verifier) { 389 this.verifier = verifier; 390 } 391 392 public static String getUsage() { 393 return "Usage: RetroWeaver " + newLine + " <source path>" + newLine 394 + " [<output path>]"; 395 } 396 397 public static void main(String[] args) { 398 399 if (args.length < 1) { 400 System.out.println(getUsage()); // NOPMD by xlv 401 return; 402 } 403 404 String sourcePath = args[0]; 405 String outputPath = null; 406 407 if (args.length > 1) { 408 outputPath = args[1]; 409 } 410 411 try { 412 RetroWeaver weaver = new RetroWeaver(Weaver.VERSION_1_4); 413 weaver.setListener(new DefaultWeaveListener(false)); 414 weaver.weave(sourcePath, outputPath); 415 } catch (Exception e) { 416 e.printStackTrace(); 417 } 418 } 419 420 /** 421 * @param stripSignatures The stripSignatures to set. 422 */ 423 public void setStripSignatures(boolean stripSignatures) { 424 this.stripSignatures = stripSignatures; 425 } 426 427 /** 428 * @param stripAttributes the stripAttributes to set 429 */ 430 public void setStripAttributes(boolean stripAttributes) { 431 this.stripAttributes = stripAttributes; 432 } 433 434 public void addNameSpaces(List<NameSpace> nameSpaces) { 435 NameTranslator translator = NameTranslator.getGeneralTranslator(); 436 for(NameSpace n: nameSpaces) { 437 translator.addNameSpace(n); 438 } 439 } 440 441 } 442 443 class LazyException extends RuntimeException { 444 } 445 446 class ClassWeaver extends ClassAdapter implements Opcodes { 447 448 private final boolean lazy; 449 private final boolean stripAttributes; 450 private final int target; 451 private int originalClassVersion; 452 private final WeaveListener listener; 453 454 private String className; 455 456 private boolean isEnum; 457 private boolean isInterface; 458 459 private final Set<String> classLiteralCalls = new HashSet<String>(); 460 461 public ClassWeaver(final ClassVisitor cv, boolean lazy, boolean stripAttributes, int target, WeaveListener listener) { 462 super(cv); 463 this.lazy = lazy; 464 this.stripAttributes = stripAttributes; 465 this.target = target; 466 this.listener = listener; 467 } 468 469 public void visit( 470 final int version, 471 final int access, 472 final String name, 473 final String signature, 474 final String superName, 475 final String[] interfaces) 476 { 477 if (lazy && (version <= target)) { 478 // abort all visitors 479 throw new LazyException(); 480 } 481 482 if (listener != null) { 483 listener.weavingPath(name); 484 } 485 486 className = name; 487 isEnum = superName != null && superName.equals("java/lang/Enum"); 488 isInterface = (access & ACC_INTERFACE) == ACC_INTERFACE; 489 originalClassVersion = version; 490 491 cv.visit(target, // Changes the format of the class file from 1.5 to the target value. 492 access, 493 name, 494 signature, 495 superName, 496 interfaces); 497 } 498 499 public void visitInnerClass( 500 final String name, 501 final String outerName, 502 final String innerName, 503 final int access) 504 { 505 cv.visitInnerClass(name, outerName, innerName, access); 506 } 507 508 public FieldVisitor visitField( 509 final int access, 510 final String name, 511 final String desc, 512 final String signature, 513 final Object value) 514 { 515 return cv.visitField(access, name, desc, signature, value); 516 } 517 518 public MethodVisitor visitMethod( 519 final int access, 520 final String name, 521 final String desc, 522 final String signature, 523 final String[] exceptions) 524 { 525 int newAccess; 526 if ((access&(ACC_SYNTHETIC|ACC_BRIDGE)) == (ACC_SYNTHETIC|ACC_BRIDGE)) { 527 /* 528 bridge methods for generic create problems with RMIC code in 1.4. 529 It's a known bug with 1.4, see SUN's bug database at: 530 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4811083 531 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5035300 532 533 Problem found when implementing Comparable<E>, with bridge method 534 compareTo(Ljava/lang/Object;)I; 535 */ 536 537 // Workaround disabled so that isSynthethic() and isBridge() can be implemented 538 //newAccess = access & ~ACC_SYNTHETIC & ~ACC_BRIDGE; 539 newAccess = access; 540 541 if (name.equals(APPEND_METHOD) && 542 (desc.equals(APPENDABLE_APPEND_SIGNATURE1) || 543 desc.equals(APPENDABLE_APPEND_SIGNATURE2) || 544 desc.equals(APPENDABLE_APPEND_SIGNATURE3))) { 545 /* remove bridge methods for Appendable, see Writer test case */ 546 return null; 547 } 548 } else { 549 newAccess = access; 550 } 551 552 String[] newExceptions; 553 if (exceptions != null) { 554 newExceptions = new String[exceptions.length]; 555 for (int i = 0; i < exceptions.length; i++) { 556 newExceptions[i] = NameTranslator.getGeneralTranslator().getClassMirrorTranslation(exceptions[i]); 557 } 558 } else { 559 newExceptions = exceptions; 560 } 561 MethodVisitor mv = new MethodWeaver(super.visitMethod(newAccess, 562 name, 563 desc, 564 signature, 565 newExceptions)); 566 567 if (!isEnum || !"<clinit>".equals(name)) { 568 return mv; 569 } 570 571 return new EnumMethodWeaver(mv); 572 } 573 574 public void visitAttribute(final Attribute attr) { 575 if (attr instanceof RetroWeaverAttribute) { 576 // make sure the original version is kept if class file 577 // is weaved more than once 578 RetroWeaverAttribute ra = (RetroWeaverAttribute) attr; 579 originalClassVersion = ra.getOriginalClassVersion(); 580 } else { 581 cv.visitAttribute(attr); 582 } 583 } 584 585 public void visitEnd() { 586 if (isEnum) { 587 cv.visitField(ACC_PRIVATE + ACC_STATIC + ACC_FINAL + ACC_SYNTHETIC, 588 SERIAL_ID_FIELD, 589 SERIAL_ID_SIGNATURE, 590 null, new Long(0L)); 591 } 592 if (!classLiteralCalls.isEmpty()) { 593 // generate synthetic fields and class$ method 594 for(String fieldName: classLiteralCalls) { 595 FieldVisitor fv = visitField(ACC_STATIC + ACC_SYNTHETIC + ACC_FINAL 596 + (isInterface?ACC_PUBLIC:ACC_PRIVATE), 597 fieldName, 598 CLASS_FIELD_DESC, 599 null, null); 600 fv.visitEnd(); 601 } 602 } 603 604 if (!stripAttributes) { 605 RetroWeaverAttribute a = new RetroWeaverAttribute(Weaver.getBuildNumber(), originalClassVersion); 606 cv.visitAttribute(a); 607 } 608 609 cv.visitEnd(); 610 } 611 612 /** 613 * Generate the byte code equivalent to ".class" 614 * 615 * @param mv method visitor to use 616 * @param cls name of class 617 */ 618 private void generateClassCall(MethodVisitor mv, String cls) { 619 /* 620 * generate the code equivalent to ".class" 621 * 622 623 new cls[0].getClass().getComponentType() 624 */ 625 626 mv.visitInsn (ICONST_0); 627 mv.visitTypeInsn (ANEWARRAY, cls); 628 mv.visitMethodInsn (INVOKEVIRTUAL, JAVA_LANG_OBJECT, GET_CLASS_METHOD, GET_CLASS_SIGNATURE); 629 mv.visitMethodInsn (INVOKEVIRTUAL, JAVA_LANG_CLASS, GET_COMPONENT_TYPE_METHOD, GET_COMPONENT_TYPE_SIGNATURE); 630 } 631 632 private class EnumMethodWeaver extends MethodAdapter implements Opcodes { 633 public EnumMethodWeaver(final MethodVisitor mv) { 634 super(mv); 635 } 636 637 public void visitInsn(final int opcode) { 638 if (opcode == RETURN) { 639 // add call to setEnumValues(Object[] values, Class c) 640 641 String owner = className.replace('.', '/'); 642 String fullName = 'L' + owner + ';'; 643 Type t = Type.getType(fullName); 644 645 mv.visitMethodInsn(INVOKESTATIC, owner, "values", "()[" + fullName); 646 mv.visitLdcInsn(t); 647 mv.visitMethodInsn( INVOKESTATIC, RETROWEAVER_ENUM, 648 "setEnumValues", "([Ljava/lang/Object;Ljava/lang/Class;)V" ); 649 } 650 mv.visitInsn(opcode); 651 } 652 653 } 654 655 private static final String JAVA_LANG_CLASS = "java/lang/Class"; 656 657 private static final String JAVA_LANG_OBJECT = "java/lang/Object"; 658 private static final String GET_CLASS_METHOD = "getClass"; 659 private static final String GET_CLASS_SIGNATURE = "()Ljava/lang/Class;"; 660 private static final String GET_COMPONENT_TYPE_METHOD = "getComponentType"; 661 private static final String GET_COMPONENT_TYPE_SIGNATURE = "()Ljava/lang/Class;"; 662 663 private static final String SERIAL_ID_FIELD = "serialVersionUID"; 664 private static final String SERIAL_ID_SIGNATURE = "J"; 665 666 private static final String CLASS_FIELD_DESC = "Ljava/lang/Class;"; 667 668 private static final String ITERABLE_CLASS = "java/lang/Iterable"; 669 private static final String ITERATOR_METHOD = "iterator"; 670 private static final String ITERATOR_SIGNATURE = "()Ljava/util/Iterator;"; 671 private static final String ITERABLE_METHODS_CLASS = "net/sourceforge/retroweaver/runtime/java/lang/Iterable_"; 672 private static final String ITERABLE_METHODS_ITERATOR_SIGNATURE = "(Ljava/lang/Object;)Ljava/util/Iterator;"; 673 674 private static final String APPEND_METHOD = "append"; 675 private static final String APPENDABLE_APPEND_SIGNATURE1 = "(C)Ljava/lang/Appendable;"; 676 private static final String APPENDABLE_APPEND_SIGNATURE2 = "(Ljava/lang/CharSequence;II)Ljava/lang/Appendable;"; 677 private static final String APPENDABLE_APPEND_SIGNATURE3 = "(Ljava/lang/CharSequence;)Ljava/lang/Appendable;"; 678 679 private static final String RETROWEAVER_ENUM = "net/sourceforge/retroweaver/runtime/java/lang/Enum"; 680 681 private static final String REENTRANTREADWRITELOCK_CLASS = "java/util/concurrent/locks/ReentrantReadWriteLock"; 682 private static final String REENTRANTREADWRITELOCK_READLOCK_CLASS = "java/util/concurrent/locks/ReentrantReadWriteLock$ReadLock"; 683 private static final String REENTRANTREADWRITELOCK_WRITELOCK_CLASS = "java/util/concurrent/locks/ReentrantReadWriteLock$WriteLock"; 684 private static final String READLOCK_METHOD = "readLock"; 685 private static final String WRITELOCK_METHOD = "writeLock"; 686 private static final String REENTRANTREADWRITELOCK_READLOCK_SIGNATURE = "()Ljava/util/concurrent/locks/ReentrantReadWriteLock$ReadLock;"; 687 private static final String REENTRANTREADWRITELOCK_WRITELOCK_SIGNATURE = "()Ljava/util/concurrent/locks/ReentrantReadWriteLock$WriteLock;"; 688 private static final String REENTRANTREADWRITELOCK_READLOCK_NEW_SIGNATURE = "()Ljava/util/concurrent/locks/Lock;"; 689 private static final String REENTRANTREADWRITELOCK_WRITELOCK_NEW_SIGNATURE = "()Ljava/util/concurrent/locks/Lock;"; 690 691 class MethodWeaver extends MethodAdapter implements Opcodes { 692 693 public MethodWeaver(final MethodVisitor mv) { 694 super(mv); 695 } 696 697 public void visitMethodInsn( 698 final int opcode, 699 final String owner, 700 final String name, 701 final String desc) 702 { 703 if (opcode == INVOKEINTERFACE && 704 owner.equals(ITERABLE_CLASS) && 705 name.equals(ITERATOR_METHOD) && 706 desc.equals(ITERATOR_SIGNATURE)) { 707 super.visitMethodInsn(INVOKESTATIC, 708 ITERABLE_METHODS_CLASS, 709 ITERATOR_METHOD, 710 ITERABLE_METHODS_ITERATOR_SIGNATURE); 711 return; 712 } else if (opcode == INVOKEVIRTUAL && 713 owner.equals(REENTRANTREADWRITELOCK_CLASS)) { 714 // workaround for ReentrantReadWriteLock readLock() and writeLock() incompatible return types 715 if (name.equals(READLOCK_METHOD) && desc.equals(REENTRANTREADWRITELOCK_READLOCK_SIGNATURE)) { 716 super.visitMethodInsn(opcode, owner, name, REENTRANTREADWRITELOCK_READLOCK_NEW_SIGNATURE); 717 super.visitTypeInsn(CHECKCAST, REENTRANTREADWRITELOCK_READLOCK_CLASS); 718 return; 719 } else if (name.equals(WRITELOCK_METHOD) && desc.equals(REENTRANTREADWRITELOCK_WRITELOCK_SIGNATURE)) { 720 super.visitMethodInsn(opcode, owner, name, REENTRANTREADWRITELOCK_WRITELOCK_NEW_SIGNATURE); 721 super.visitTypeInsn(CHECKCAST, REENTRANTREADWRITELOCK_WRITELOCK_CLASS); 722 return; 723 } 724 } 725 726 // not a special case, use default implementation 727 super.visitMethodInsn(opcode, owner, name, desc); 728 } 729 730 731 public void visitLdcInsn(final Object cst) { 732 if (cst instanceof Type) { 733 /** 734 * Fix class literals. The 1.5 VM has had its ldc* instructions updated so 735 * that it knows how to deal with CONSTANT_Class in addition to the other 736 * types. So, we have to search for uses of ldc* that point to a 737 * CONSTANT_Class and replace them with synthetic field access the way 738 * it was generated in 1.4. 739 */ 740 741 // LDC or LDC_W with a class as argument 742 743 Type t = (Type) cst; 744 String fieldName = getClassLiteralFieldName(t); 745 746 classLiteralCalls.add(fieldName); 747 748 mv.visitFieldInsn(GETSTATIC, className, fieldName, CLASS_FIELD_DESC); 749 mv.visitInsn(DUP); 750 Label nonNullLabel = new Label(); 751 mv.visitJumpInsn(IFNONNULL, nonNullLabel); 752 mv.visitInsn(POP); 753 String s; 754 if (t.getSort() == Type.OBJECT) { 755 s = t.getInternalName(); 756 } else { 757 s = t.getDescriptor(); 758 } 759 760 /* convert retroweaver runtime classes: 761 * Enum into net.sourceforge.retroweaver.runtime.Enum_ 762 * concurrent classes into their backport equivalent 763 * ... 764 */ 765 s = NameTranslator.getGeneralTranslator().getClassMirrorTranslationDescriptor(s); 766 s = NameTranslator.getStringBuilderTranslator().getClassMirrorTranslationDescriptor(s); 767 768 generateClassCall(mv, s); 769 mv.visitInsn(DUP); 770 mv.visitFieldInsn(PUTSTATIC, className, fieldName, CLASS_FIELD_DESC); 771 mv.visitLabel(nonNullLabel); 772 } else { 773 super.visitLdcInsn(cst); 774 } 775 } 776 777 private String getClassLiteralFieldName(Type type) { 778 String fieldName; 779 if (type.getSort() == Type.ARRAY) { 780 fieldName = "array" + type.getDescriptor().replace('[', '$'); 781 if (fieldName.charAt(fieldName.length()-1) == ';') { 782 fieldName = fieldName.substring(0, fieldName.length()-1); 783 } 784 } else { 785 fieldName = "class$" + type.getInternalName(); 786 } 787 fieldName = fieldName.replace('/', '$'); 788 789 return fieldName; 790 } 791 792 } 793 794 } 795 796 class DefaultWeaveListener implements WeaveListener { 797 798 private final boolean verbose; 799 800 DefaultWeaveListener(boolean verbose) { 801 this.verbose = verbose; 802 } 803 804 public void weavingStarted(String msg) { 805 System.out.println("[RetroWeaver] " + msg); // NOPMD by xlv 806 } 807 808 public void weavingCompleted(String msg) { 809 System.out.println("[RetroWeaver] " + msg); // NOPMD by xlv 810 } 811 812 public void weavingError(String msg) { 813 System.out.println("[RetroWeaver] " + msg); // NOPMD by xlv 814 } 815 816 public void weavingPath(String sourcePath) { 817 if (verbose) { 818 System.out.println("[RetroWeaver] Weaving " + sourcePath); // NOPMD by xlv 819 } 820 } 821 } 822