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