package net.sourceforge.retroweaver.translator; import java.util.HashSet; import java.util.Set; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassAdapter; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodAdapter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.signature.SignatureReader; import org.objectweb.asm.signature.SignatureWriter; public class NameTranslatorClassVisitor extends ClassAdapter { private final NameTranslator translator; public NameTranslatorClassVisitor(final ClassVisitor classVisitor, final NameTranslator translator) { super(classVisitor); this.translator = translator; } private Set<String> visitedMethods; private String className; private String translateSignature(final String signature, boolean type) { if (signature == null) { return null; } SignatureReader r = new SignatureReader(signature); SignatureWriter w = new SignatureWriter() { public void visitClassType(final String name) { String n = translator.getClassMirrorTranslation(name); super.visitClassType(n); } }; if (type) { r.acceptType(w); } else { r.accept(w); } return w.toString(); } public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) { final String newSuperName = translator.getClassMirrorTranslation(superName); String newInterfaces[] = new String[interfaces.length]; for (int i = 0; i < interfaces.length; i++) { newInterfaces[i] = translator.getClassMirrorTranslation(interfaces[i]); } className = name; visitedMethods = new HashSet<String>(); super.visit(version, access, name, translateSignature(signature, false), newSuperName, newInterfaces); } public FieldVisitor visitField(final int access, final String name, final String desc, final String signature, final Object value) { return super.visitField(access, name, translator.getClassMirrorTranslationDescriptor(desc), translateSignature(signature, true), value); } public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) { final String newDesc = translator.translateMethodDescriptor(desc); final String fullDesc = name + newDesc; if (visitedMethods.contains(fullDesc)) { throw new TranslatorException( "Duplicate method after name translation in class " + className + ": " + name + ' ' + newDesc); } visitedMethods.add(fullDesc); MethodVisitor mv = super.visitMethod(access, name, newDesc, translateSignature(signature, false), exceptions); return (mv == null)?null:new MethodTranslator(mv); } public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) { final String newDesc = translateAnnotationDescriptor(desc); return new AnnotationTranslator(cv.visitAnnotation(newDesc, visible)); } private String translateAnnotationDescriptor(final String name) { if ((name == null) || (name.length() == 0)) { return name; } if ((name.charAt(0) != 'L') || (name.charAt(name.length()-1) != ';')) { return name; } return translator.translateDescriptor(name); } private class MethodTranslator extends MethodAdapter { MethodTranslator(MethodVisitor methodVisitor) { super(methodVisitor); } public void visitTypeInsn(final int opcode, final String desc) { super.visitTypeInsn(opcode, translator.getClassMirrorTranslationDescriptor(desc)); } public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) { String newDesc = translator.getClassMirrorTranslationDescriptor(desc); if (opcode == Opcodes.GETSTATIC) { final Mirror mirror = translator.getMirror(owner); if (mirror.hasStaticField(name, newDesc)) { super.visitFieldInsn(opcode, translator.translate(owner), name, newDesc); return; } } super.visitFieldInsn(opcode, translator.getClassMirrorTranslation(owner), name, newDesc); } public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) { // Special case method invocations for name translation. // Specifically to deal with methods mirrors. String newOwner = owner; int newOpcode = opcode; String newDesc = translator.translateMethodDescriptor(desc); String lookupOwner = owner; while (lookupOwner.startsWith("[")) { lookupOwner = lookupOwner.substring(1); } final Mirror mirror = translator.getMirror(lookupOwner); if (mirror.isClassMirror()) { newOwner = translator.translate(owner); } else if ("<init>".equals(name)&&(opcode == Opcodes.INVOKESPECIAL)) { /* Look for an equivalent constructor. For instance, * INVOKESPECIAL, "java/math/BigDecimal", "<init>", "(I)V") * will be transformed as * INVOKESTATIC, "../BigDecimal_", "BigDecimal", "(I)Ljava/math/BigDecimal;" * * the previously constructed object was on top of the stack and the mirror * function has put its result on top, so a SWAP and POP are issued to * discard the previously constructed object and store the new one instead. */ String constructorDesc = newDesc.substring(0, newDesc.length()-1) + 'L' + owner + ';'; String constructorName; int i = owner.lastIndexOf('/'); if (i == -1) { constructorName = owner; } else { constructorName = owner.substring(i+1); } if (mirror.hasMethod(owner, constructorName, constructorDesc, Opcodes.INVOKESPECIAL)) { newOwner = translator.translate(owner); super.visitMethodInsn(Opcodes.INVOKESTATIC, newOwner, constructorName, constructorDesc); super.visitInsn(Opcodes.SWAP); super.visitInsn(Opcodes.POP); super.visitInsn(Opcodes.SWAP); super.visitInsn(Opcodes.POP); return; } } else if (mirror.hasMethod(owner, name, newDesc, opcode)) { newOwner = translator.translate(owner); newOpcode = Opcodes.INVOKESTATIC; // We have to insert the owner into the arguments of the // descriptor if (opcode == Opcodes.INVOKEVIRTUAL || opcode == Opcodes.INVOKEINTERFACE) { final Type[] argTypes = Type.getArgumentTypes(newDesc); final Type[] newArgTypes = new Type[argTypes.length + 1]; newArgTypes[0] = Type.getType("L" + owner + ";"); System.arraycopy(argTypes, 0, newArgTypes, 1, argTypes.length); newDesc = Type.getMethodDescriptor( Type.getReturnType(newDesc), newArgTypes); newDesc = translator.translateMethodDescriptor(newDesc); } } super.visitMethodInsn(newOpcode, newOwner, name, newDesc); } public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { super.visitTryCatchBlock(start, end, handler, translator.translate(type)); } public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, final int index) { super.visitLocalVariable(name, translator.translateDescriptor(desc), translateSignature(signature, true), start, end, index); } public AnnotationVisitor visitAnnotationDefault() { return new AnnotationTranslator(mv.visitAnnotationDefault()); } public AnnotationVisitor visitAnnotation( final String desc, final boolean visible) { String newDesc = translator.getClassMirrorTranslationDescriptor(desc); return new AnnotationTranslator(mv.visitAnnotation(newDesc, visible)); } public AnnotationVisitor visitParameterAnnotation( final int parameter, final String desc, final boolean visible) { String newDesc = translator.getClassMirrorTranslationDescriptor(desc); return new AnnotationTranslator(mv.visitParameterAnnotation(parameter, newDesc, visible)); } } private class AnnotationTranslator implements AnnotationVisitor { private final AnnotationVisitor av; AnnotationTranslator(final AnnotationVisitor av) { this.av = av; } public void visit(final String name, final Object value) { final String newName = translateAnnotationDescriptor(name); av.visit(newName, value); } public void visitEnum(final String name, final String desc, final String value) { final String newName = translateAnnotationDescriptor(name); final String newDesc = translateAnnotationDescriptor(desc); av.visitEnum(newName, newDesc, value); } public AnnotationVisitor visitAnnotation(final String name, final String desc) { final String newName = translateAnnotationDescriptor(name); return new AnnotationTranslator(av.visitAnnotation(newName, desc)); } public AnnotationVisitor visitArray(final String name) { final String newName = translateAnnotationDescriptor(name); return new AnnotationTranslator(av.visitArray(newName)); } public void visitEnd() { av.visitEnd(); } } }