Momo
Momo

Reputation: 3810

Overriding a Local Variable name in Java Bytecode using the ASM library

I read a class' methods and their contents using the ASM library for Java ByteCode. This is to output any method's local variables names in the class:

ClassReader reader = new ClassReader(new FileInputStream(new File("TheClass.class")));

final ClassNode classNode = new ClassNode();
reader.accept(classNode, 0);

for (final MethodNode mn : (List<MethodNode>)classNode.methods) {
    for (LocalVariableNode n : (List<LocalVariableNode>)mn.localVariables) {
        System.out.println(n.name);
    }
}

And this is the source of the compiled TheClass class file:

public class TheClass {
    public final String a;
    public final String b;

    public TheClass(String c, String d) {
        this.b = d;
        this.a = c;
    }
}

So the output is, logically, this, c, d. Now, I need to copy this compiled class into a new file, but change the <init> method's parameters (local variables) to different names (e, f). How can I do so? I have little to no experience with MethodVisitors and such.

Upvotes: 4

Views: 1443

Answers (1)

dejvuth
dejvuth

Reputation: 7136

You'll need to write an adapter (a subclass of ClassVisitor) and chain it with reader. For instance,

ClassReader reader = new ClassReader(new FileInputStream(new File("TheClass")));
ClassWriter writer = new ClassWriter(reader, 0);
TraceClassVisitor printer = new TraceClassVisitor(writer, 
    new PrintWriter(System.getProperty("java.io.tmpdir") 
            + File.separator + name + ".log"));
ClassAdapter adapter = new ClassAdapter(printer);
reader.accept(adapter, 0);
byte[] b = writer.toByteArray();

With it you'll get byte[], which you can save into file, or load into Class with a ClassLoader.

(TraceClassVisitor is just another ClassVisitor that I chain it also to get a human-readable log in your temp directory.)

The adapter could look like the following. The method you'll want to override is visitLocalVariable:

public class ClassAdapter extends ClassVisitor {
    public ClassAdapter(ClassVisitor cv) {
        super(Opcodes.ASM5, cv);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc,
            String signature, String[] exceptions) {
        MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
        return new MethodAdapter(mv);
    }
}

public class MethodAdapter extends MethodVisitor {
    public MethodAdapter(MethodVisitor mv) {
        super(Opcodes.ASM5, mv);
    }

    @Override
    public void visitLocalVariable(String name, String desc, String signature,
            Label start, Label end, int index) {
        // Put your rename logic here
        if (name.equals("c"))
            name = "e";
        else if (name.equals("d"))
            name = "f";
        super.visitLocalVariable(name, desc, signature, start, end, index);
    }
}

Upvotes: 4

Related Questions