Skip to content

Commit f2e4e52

Browse files
Write ReturnInsnProcessor tests
1 parent f0ef5b8 commit f2e4e52

File tree

2 files changed

+89
-46
lines changed

2 files changed

+89
-46
lines changed

newrelic-weaver/src/main/java/com/newrelic/weave/utils/ReturnInsnProcessor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ private static InsnList insnsBeforeReturn(int opcode, int stackSize, int varInde
7474
}
7575

7676
/*
77-
* Compute the size of the operand stack at each return instruction.
77+
* Compute the size of the operand stack at each return instruction exceeding the EXPECTED_RETURN_STACK_SIZE of 1.
7878
* Only applies to methods returning I (int type) or A (reference type).
7979
*
8080
* @param owner The owning class
@@ -88,7 +88,7 @@ private static Map<AbstractInsnNode, Integer> getReturnStacks(String owner, Meth
8888
Map<AbstractInsnNode, Integer> rtStacks = new HashMap<>();
8989
for (int j = 0; j < method.instructions.size(); ++j) {
9090
AbstractInsnNode insn = method.instructions.get(j);
91-
if (insn.getOpcode() == Opcodes.IRETURN && insn.getOpcode() == Opcodes.ARETURN) {
91+
if (insn.getOpcode() == Opcodes.IRETURN || insn.getOpcode() == Opcodes.ARETURN) {
9292
Frame<BasicValue> f = frames[j];
9393
if (f != null && f.getStackSize() > EXPECTED_RETURN_STACK_SIZE) {
9494
rtStacks.put(insn, f.getStackSize());
Lines changed: 87 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,117 @@
11
package com.newrelic.weave.utils;
22

3-
import com.sun.org.apache.bcel.internal.generic.ALOAD;
3+
import net.bytebuddy.asm.Advice;
44
import org.junit.Test;
55
import org.objectweb.asm.Opcodes;
66
import org.objectweb.asm.Type;
77
import org.objectweb.asm.tree.*;
88

9-
import static org.junit.Assert.*;
10-
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
11-
import static org.objectweb.asm.Type.DOUBLE_TYPE;
12-
import static org.objectweb.asm.Type.INT_TYPE;
13-
14-
public class ReturnInsnProcessorTest {
9+
import java.util.Iterator;
1510

11+
import static org.junit.Assert.*;
12+
import static org.objectweb.asm.Opcodes.*;
1613

1714

15+
public class ReturnInsnProcessorTest {
1816
//the nodes in here need to be constructed by hand since Java compilers don't have this issue
19-
2017
@Test
21-
public void singleReturnStackClears() {
22-
MethodNode mn = buildMethodNode(INT_TYPE);
23-
buildOneUntidyReturn(mn);
24-
WeaveUtils.printAllInstructions(mn);
25-
26-
ReturnInsnProcessor.clearReturnStacks("MyClazz", mn);
27-
WeaveUtils.printAllInstructions(mn);
18+
public void doesNotAlterAlreadyClearStack(){
19+
int[] originalSeq = {ICONST_1, DUP, POP, IRETURN};
20+
MethodNode mn = basicMethodFromSequence(Type.INT, originalSeq, 2);
21+
ReturnInsnProcessor.clearReturnStacks("MyClazz", mn);
22+
assertInsnSequence(originalSeq, mn.instructions);
23+
24+
int[] originalSeqRefType = {ACONST_NULL, ARETURN};
25+
MethodNode mn2 = basicMethodFromSequence(Type.OBJECT, originalSeqRefType, 1);
26+
ReturnInsnProcessor.clearReturnStacks("MyClazz", mn2);
27+
assertInsnSequence(originalSeqRefType, mn2.instructions);
2828
}
2929

3030
@Test
31-
public void stackAlreadyClearDoesNothing() {
31+
public void addsPopsForIntReturnType(){
32+
int[] originalInsnSequence = {ICONST_2, ICONST_1, DUP, DUP, ICONST_3, IRETURN};
33+
MethodNode mn = basicMethodFromSequence(Type.INT, originalInsnSequence, 5);
3234

35+
ReturnInsnProcessor.clearReturnStacks("MyClazz", mn);
36+
int[] expectedInsnSequence = {ICONST_2, ICONST_1, DUP, DUP, ICONST_3, ISTORE, POP, POP, POP, POP, ILOAD, IRETURN};
37+
assertInsnSequence(expectedInsnSequence, mn.instructions);
3338
}
3439

40+
@Test
41+
public void addsPopsForReferenceReturnType(){
42+
int[] originalInsnSequence = {ACONST_NULL, DUP, DUP, DUP, ARETURN};
43+
MethodNode mn = basicMethodFromSequence(Type.OBJECT, originalInsnSequence, 4);
3544

36-
public MethodNode buildMethodNode(Type type) {
37-
String desc = "";
38-
desc = "()" + type.getDescriptor();
39-
MethodNode mn = new MethodNode(Opcodes.ASM9, ACC_PUBLIC, "myMethod", desc, null, null);
40-
return mn;
45+
ReturnInsnProcessor.clearReturnStacks("MyClazz", mn);
46+
int[] expectedInsnSequence = {ACONST_NULL, DUP, DUP, DUP, ASTORE, POP, POP, POP, ALOAD, ARETURN};
47+
assertInsnSequence(expectedInsnSequence, mn.instructions);
4148
}
4249

43-
private void buildOneUntidyReturn(MethodNode mn) {
44-
//Java compilers will resist untidy return stacks.
45-
//But we can generate unnecessary bytecode using ASM if we want to!
50+
@Test
51+
public void handlesMultipleReturns(){
52+
//This Insn List is more complicated, so we have to build it directly here
4653
InsnList insns = new InsnList();
47-
//setup insns
48-
insns.add(new LabelNode());
49-
insns.add(new InsnNode(Opcodes.DCONST_1));
50-
insns.add(new VarInsnNode(Opcodes.DSTORE, 1));
51-
insns.add(new InsnNode(Opcodes.DCONST_0));
52-
insns.add(new InsnNode(Opcodes.DUP2));
53-
insns.add(new InsnNode(Opcodes.DRETURN));
54-
54+
insns.add(new InsnNode(ICONST_1));
55+
insns.add(new InsnNode(DUP));
56+
LabelNode label = new LabelNode();
57+
insns.add(new JumpInsnNode(IFEQ, label));
58+
insns.add(new InsnNode(IRETURN));
59+
insns.add(label);
60+
insns.add(new InsnNode(DUP));
61+
insns.add(new InsnNode(IRETURN));
62+
MethodNode mn = buildMethodNode(Type.INT);
5563
mn.instructions.add(insns);
56-
mn.maxLocals += 4;
57-
mn.maxStack += 4;
58-
}
59-
60-
private void buildOneTidyReturn(MethodNode mn) {
61-
InsnList insns = new InsnList();
62-
//setup insns
63-
insns.add(new InsnNode(Opcodes.ICONST_3));
64-
insns.add(new VarInsnNode(Opcodes.ISTORE, 1));
65-
insns.add(new InsnNode(Opcodes.ICONST_4));
66-
insns.add(new InsnNode(Opcodes.IRETURN));
64+
mn.maxLocals = 1;
65+
mn.maxStack = 2;
6766

68-
mn.instructions.add(insns);
67+
ReturnInsnProcessor.clearReturnStacks("MyClazz", mn);
68+
int[] expectedInsns = {ICONST_1, DUP, IFEQ, IRETURN, -1, DUP, ISTORE, POP, ILOAD, IRETURN};
69+
assertInsnSequence(expectedInsns, mn.instructions);
6970
}
7071

72+
@Test
73+
public void failedAnalyzerDoesNothing() {
74+
//these instructions are bad and will fail the analyzer - we shouldn't touch them.
75+
int[] originalInsns = {ICONST_1, POP, POP, IRETURN};
76+
MethodNode mn = basicMethodFromSequence(Type.INT, originalInsns, 1);
77+
ReturnInsnProcessor.clearReturnStacks("MyClazz", mn);
78+
assertInsnSequence(originalInsns, mn.instructions);
79+
}
7180

81+
private void assertInsnSequence( int[] expectedSequence, InsnList insns) {
82+
Iterator<AbstractInsnNode> it = insns.iterator();
83+
int i = 0;
84+
while (it.hasNext()) {
85+
AbstractInsnNode insn = it.next();
86+
int expectedOpcode = expectedSequence[i];
87+
assertEquals(expectedOpcode, insn.getOpcode());
88+
i++;
89+
}
90+
}
7291

92+
private MethodNode basicMethodFromSequence(int type, int[] insnSequence, int maxStack) {
93+
MethodNode mn = buildMethodNode(type);
94+
for(int opcode: insnSequence){
95+
mn.instructions.add(new InsnNode(opcode));
96+
}
97+
mn.maxStack = maxStack;
98+
mn.maxLocals = 1;
99+
return mn;
100+
}
73101

102+
public MethodNode buildMethodNode(int type) {
103+
String desc = "";
104+
switch (type) {
105+
case Type.INT:
106+
desc = "()I";
107+
break;
108+
case Type.OBJECT:
109+
desc = "()L";
110+
break;
111+
default:
112+
fail();
113+
}
114+
MethodNode mn = new MethodNode(Opcodes.ASM9, ACC_PUBLIC, "myMethod", desc, null, null);
115+
return mn;
116+
}
74117
}

0 commit comments

Comments
 (0)