001    package net.sourceforge.retroweaver.translator;
002    
003    import java.util.HashSet;
004    import java.util.Set;
005    
006    import org.objectweb.asm.AnnotationVisitor;
007    import org.objectweb.asm.ClassAdapter;
008    import org.objectweb.asm.ClassVisitor;
009    import org.objectweb.asm.FieldVisitor;
010    import org.objectweb.asm.Label;
011    import org.objectweb.asm.MethodAdapter;
012    import org.objectweb.asm.MethodVisitor;
013    import org.objectweb.asm.Opcodes;
014    import org.objectweb.asm.Type;
015    import org.objectweb.asm.signature.SignatureReader;
016    import org.objectweb.asm.signature.SignatureWriter;
017    
018    public class NameTranslatorClassVisitor extends ClassAdapter {
019    
020            private final NameTranslator translator;
021    
022            public NameTranslatorClassVisitor(final ClassVisitor classVisitor, final NameTranslator translator) {
023                    super(classVisitor);
024                    this.translator = translator;
025            }
026    
027            private Set<String> visitedMethods;
028    
029            private String className;
030    
031            private String translateSignature(final String signature, boolean type) {
032                    if (signature == null) {
033                            return null;
034                    }
035                    SignatureReader r = new SignatureReader(signature);
036                    SignatureWriter w = new SignatureWriter() {
037                        public void visitClassType(final String name) {
038                            String n = translator.getClassMirrorTranslation(name);
039                            super.visitClassType(n);
040                        }
041                    };
042    
043                    if (type) {
044                            r.acceptType(w);                
045                    } else {
046                            r.accept(w);            
047                    }
048                    return w.toString();
049            }
050    
051            public void visit(final int version, final int access, final String name,
052                            final String signature, final String superName,
053                            final String[] interfaces) {
054                    final String newSuperName = translator.getClassMirrorTranslation(superName);
055    
056                    String newInterfaces[] = new String[interfaces.length];
057                    for (int i = 0; i < interfaces.length; i++) {
058                            newInterfaces[i] = translator.getClassMirrorTranslation(interfaces[i]);
059                    }
060    
061                    className = name;
062                    visitedMethods = new HashSet<String>();
063    
064                    super.visit(version, access, name, translateSignature(signature, false), newSuperName, newInterfaces);
065            }
066    
067            public FieldVisitor visitField(final int access, final String name,
068                            final String desc, final String signature, final Object value) {
069                    return super.visitField(access, name,
070                                                                    translator.getClassMirrorTranslationDescriptor(desc),
071                                                                    translateSignature(signature, true), value);
072            }
073    
074            public MethodVisitor visitMethod(final int access, final String name,
075                            final String desc, final String signature, final String[] exceptions) {
076    
077                    final String newDesc = translator.translateMethodDescriptor(desc);
078                    final String fullDesc = name + newDesc;
079                    if (visitedMethods.contains(fullDesc)) {
080                            throw new TranslatorException(
081                                            "Duplicate method after name translation in class "
082                                                            + className + ": " + name + ' ' + newDesc);
083                    }
084                    visitedMethods.add(fullDesc);
085    
086                    MethodVisitor mv = super.visitMethod(access, name, newDesc,
087                                    translateSignature(signature, false), exceptions);
088                    return (mv == null)?null:new MethodTranslator(mv);
089            }
090    
091        public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
092            final String newDesc = translateAnnotationDescriptor(desc);
093            return new AnnotationTranslator(cv.visitAnnotation(newDesc, visible));
094        }
095    
096            private String translateAnnotationDescriptor(final String name) {
097                    if ((name == null) || (name.length() == 0)) {
098                            return name;
099                    }
100    
101                    if ((name.charAt(0) != 'L') || (name.charAt(name.length()-1) != ';')) {
102                            return name;
103                    }
104    
105                    return translator.translateDescriptor(name);
106            }
107    
108            private class MethodTranslator extends MethodAdapter {
109                    MethodTranslator(MethodVisitor methodVisitor) {
110                            super(methodVisitor);
111                    }
112    
113                    public void visitTypeInsn(final int opcode, final String desc) {
114                            super.visitTypeInsn(opcode, translator.getClassMirrorTranslationDescriptor(desc));
115                    }
116    
117                    public void visitFieldInsn(final int opcode, final String owner,
118                                    final String name, final String desc) {
119    
120                            String newDesc = translator.getClassMirrorTranslationDescriptor(desc);
121    
122                            if (opcode == Opcodes.GETSTATIC) {
123                                    final Mirror mirror = translator.getMirror(owner);
124                                    if (mirror.hasStaticField(name, newDesc)) {
125                                            super.visitFieldInsn(opcode,
126                                                                                            translator.translate(owner), name, newDesc);
127    
128                                            return;
129                                    }
130                            }
131                            super.visitFieldInsn(opcode,
132                                                                            translator.getClassMirrorTranslation(owner),
133                                                                            name, newDesc);
134                    }
135    
136                    public void visitMethodInsn(final int opcode, final String owner,
137                                    final String name, final String desc) {
138    
139                            // Special case method invocations for name translation.
140                            // Specifically to deal with methods mirrors.
141                            String newOwner = owner;
142                            int newOpcode = opcode;
143                            String newDesc = desc;
144    
145                            String lookupOwner = owner;
146                            while (lookupOwner.startsWith("[")) {
147                                    lookupOwner = lookupOwner.substring(1);
148                            }
149                            final Mirror mirror = translator.getMirror(lookupOwner);
150    
151                            if (mirror.isClassMirror()) {
152                                    newOwner = translator.translate(owner);
153                            } else if ("<init>".equals(name)&&(opcode == Opcodes.INVOKESPECIAL)) {
154                                    /* Look for an equivalent constructor. For instance,
155                                     * INVOKESPECIAL, "java/math/BigDecimal", "<init>", "(I)V")
156                                     * will be transformed as
157                                     * INVOKESTATIC, "../BigDecimal_", "BigDecimal", "(I)Ljava/math/BigDecimal;"
158                                     * 
159                                     * the previously constructed object was on top of the stack and the mirror
160                                     * function has put its result on top, so a SWAP and POP are issued to
161                                     * discard the previously constructed object and store the new one instead.
162                                     */
163                                    String constructorDesc = desc.substring(0, desc.length()-1) + 'L' + owner + ';';
164                                    String constructorName;
165                                    int i = owner.lastIndexOf('/');
166                                    if (i == -1) {
167                                            constructorName = owner;
168                                    } else {
169                                            constructorName = owner.substring(i+1);
170                                    }
171                                    if (mirror.hasMethod(owner, constructorName, constructorDesc, Opcodes.INVOKESPECIAL)) {
172                                            newOwner = translator.translate(owner);
173    
174                                            super.visitMethodInsn(Opcodes.INVOKESTATIC, newOwner, constructorName,
175                                                            constructorDesc);
176    
177                                            super.visitInsn(Opcodes.SWAP);
178                                            super.visitInsn(Opcodes.POP);
179                                            super.visitInsn(Opcodes.SWAP);
180                                            super.visitInsn(Opcodes.POP);
181                                            return;
182                                    }
183                            } else if (mirror.hasMethod(owner, name, desc, opcode)) {
184                                    newOwner = translator.translate(owner);
185                                    newOpcode = Opcodes.INVOKESTATIC;
186    
187                                    // We have to insert the owner into the arguments of the
188                                    // descriptor
189                                    if (opcode == Opcodes.INVOKEVIRTUAL) {
190                                            final Type[] argTypes = Type.getArgumentTypes(desc);
191                                            final Type[] newArgTypes = new Type[argTypes.length + 1];
192                                            newArgTypes[0] = Type.getType("L" + owner + ";");
193                                            System.arraycopy(argTypes, 0, newArgTypes, 1, argTypes.length);
194                                            newDesc = Type.getMethodDescriptor(
195                                                            Type.getReturnType(desc), newArgTypes);
196                                    }
197                            }
198    
199                            super.visitMethodInsn(newOpcode, newOwner, name,
200                                                                            translator.translateMethodDescriptor(newDesc));
201                    }
202    
203                    public void visitTryCatchBlock(final Label start, final Label end,
204                                    final Label handler, final String type) {
205                            super.visitTryCatchBlock(start, end, handler, translator.translate(type));
206                    }
207    
208                    public void visitLocalVariable(final String name, final String desc,
209                                    final String signature, final Label start, final Label end,
210                                    final int index) {
211                            super.visitLocalVariable(name, translator.translateDescriptor(desc),
212                                            translateSignature(signature, true), start, end, index);
213                    }
214    
215                public AnnotationVisitor visitAnnotationDefault() {
216                    return new AnnotationTranslator(mv.visitAnnotationDefault());
217                }
218    
219                public AnnotationVisitor visitAnnotation(
220                    final String desc,
221                    final boolean visible)
222                {
223                    return new AnnotationTranslator(mv.visitAnnotation(desc, visible));
224                }
225    
226                public AnnotationVisitor visitParameterAnnotation(
227                    final int parameter,
228                    final String desc,
229                    final boolean visible)
230                {
231                    return new AnnotationTranslator(mv.visitParameterAnnotation(parameter, desc, visible));
232                }
233    
234            }
235    
236            private class AnnotationTranslator implements AnnotationVisitor {
237                    
238                    private final AnnotationVisitor av;
239                    
240                    AnnotationTranslator(final AnnotationVisitor av) {
241                            this.av = av;
242                    }
243    
244                    public void visit(final String name, final Object value) {
245                            final String newName = translateAnnotationDescriptor(name);
246                            av.visit(newName, value);
247                    }
248    
249                    public void visitEnum(final String name, final String desc, final String value) {
250                            final String newName = translateAnnotationDescriptor(name);
251                            final String newDesc = translateAnnotationDescriptor(desc);
252                    av.visitEnum(newName, newDesc, value);
253                }
254    
255                    public AnnotationVisitor visitAnnotation(final String name, final String desc) {
256                            final String newName = translateAnnotationDescriptor(name);
257                    return new AnnotationTranslator(av.visitAnnotation(newName, desc));
258                }
259    
260                    public AnnotationVisitor visitArray(final String name) {
261                            final String newName = translateAnnotationDescriptor(name);
262                    return new AnnotationTranslator(av.visitArray(newName));
263                }
264    
265                    public void visitEnd() {
266                    av.visitEnd();
267                }
268    
269            }
270    }