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