Do you know the way how to clone JumpInsnNode correctly?
I tried several things:
1. endList.add(insn.clone(labelNodes));
insn is JumpInsnNode here; labelNodes - all labelNodes in the method. This code throws NullPointerException because of implementation of clone method that calls clone method from AbsractInsnNode class:
static LabelNode clone(final LabelNode label, final Map<LabelNode, LabelNode> map) {
return map.get(label);
}
This method returns null.
endList.add(new JumpInsnNode(insn.getOpcode(), new LabelNode(insn.label.getLabel())));
It throws an exception:
java.lang.ArrayIndexOutOfBoundsException: 0
at org.objectweb.asm.Frame.a(Unknown Source)
at org.objectweb.asm.Frame.a(Unknown Source)
at org.objectweb.asm.MethodWriter.visitMaxs(Unknown Source)
at org.objectweb.asm.tree.MethodNode.accept(Unknown Source)
at org.objectweb.asm.tree.MethodNode.accept(Unknown Source)
at org.objectweb.asm.tree.ClassNode.accept(Unknown Source)
at com.smartbear.real.agent.Agent.transform(Agent.java:221)
at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:424)
at sun.instrument.InstrumentationImpl.redefineClasses0(Native Method)
at sun.instrument.InstrumentationImpl.redefineClasses(InstrumentationImpl.java:170)
at com.smartbear.real.agent.Agent.agentmain(Agent.java:118)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:382)
at sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain(InstrumentationImpl.java:407)
The reasoning: I need to have ability to load method return values once again. I was able to do this with other types of instruction. However this type of instruction: return (test != null);
doesn't work for me now. Byte code for this:
LINENUMBER 112 L2
ALOAD 3: tr
IFNULL L3
ICONST_1
IRETURN
L3
FRAME APPEND [Boolean]
ICONST_0
IRETURN
It seems like you are using the wrong approach. You shouldn’t clone arbitrary instructions to replicate the return values, not only does this become unnecessarily complex, it might also replicate side-effects thus create unintended behavior different than without Instrumentation.
Replicating the value, a method is about to return, is much simpler. Right before one of the …RETURN
instructions is executed, there must be the actual return value on the operand stack. So all you have to do, is to insert an appropriate DUP
instruction (either DUP
or DUP2
) before the …RETURN
instruction to duplicate the value. Then you can insert your logging code consuming that duplicated value.
E.g. you may change occurrences of IRETURN
to:
DUP
INVOKESTATIC java/lang/Integer valueOf (I)Ljava/lang/Integer;
INVOKESTATIC MyLogger logReturnValue (Ljava/lang/Object;)V
IRETURN // the original return instruction
or DRETURN
to:
DUP2
INVOKESTATIC java/lang/Double valueOf (D)Ljava/lang/Double;
INVOKESTATIC MyLogger logReturnValue (Ljava/lang/Object;)V
DRETURN // the original return instruction
or ARETURN
to:
DUP
INVOKESTATIC MyLogger logReturnValue (Ljava/lang/Object;)V
ARETURN // the original return instruction
etc.