0

How can I rewrite a class (preferably using asm) such that a setupSpecial() invocation is introduced before every single call to special(int)?

public class Application { public void f() { System.out.println("A"); // setupSpecial(); ← to be added special(1); System.out.println("B"); // setupSpecial(); ← to be added special(2); System.out.println("C"); // setupSpecial(); ← to be added special(3); } } 

Note that both special() and setupSpecial() are not members of Application.

I can pass a ClassVisitor+MethodVisitor combination and intercept calls to special(int) via overriding visitMethodInsn(Opcodes.INVOKEVIRTUAL, "com/acme/Magic", "special", "()V", false), though this will be too late since the method argument of type integer is already passed via visitIntInsn(Opcodes.BIPUSH, int). I need a way to say "Now I see the call to special()! Roll this call back, turn magic on, and proceed again."

2

1 Answer 1

2

The JVM has a stack-based architecture, so you don't need to worry about the parameters to the special(int) call. If you simply prepend the setupSpecial() call to the special(int) call using:

@Override public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { if ("com/example/Magic".equals(owner) && "special".equals(name)) { visitMethodInsn(Opcodes.INVOKESTATIC, "com/example/Magic", "setupSpecial", Type.getMethodDescriptor(Type.VOID_TYPE), false); } super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); } 

(I assume both special and setupSpecial to be static, but it does not matter), you'll obtain bytecode similar to this:

0: getstatic #24 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #30 // String A 5: invokevirtual #32 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: iconst_1 9: invokestatic #68 // Method setupSpecial:()V 12: invokestatic #38 // Method special:(I)V 

As you can see, the parameter to the special(int) call is pushed on the stack before the call to setupSpecial(), which is unusual. However it will still be at the top of the stack once setupSpecial() has finished, so everything will work just fine as long as setupSpecial() does not return any value.

If it does, you'll need to add a pop/pop2 opcode to remove it from the stack.

Sign up to request clarification or add additional context in comments.

1 Comment

This constellation isn’t that unusual. When you have something like first(a, second(b)), the first argument to first (a) is pushed before the call to second. So only the fact that setupSpecial is void and hence, doesn’t produce an argument to special, is a bit unusual.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.