1
1
package com .newrelic .weave .utils ;
2
2
3
- import com . sun . org . apache . bcel . internal . generic . ALOAD ;
3
+ import net . bytebuddy . asm . Advice ;
4
4
import org .junit .Test ;
5
5
import org .objectweb .asm .Opcodes ;
6
6
import org .objectweb .asm .Type ;
7
7
import org .objectweb .asm .tree .*;
8
8
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 ;
15
10
11
+ import static org .junit .Assert .*;
12
+ import static org .objectweb .asm .Opcodes .*;
16
13
17
14
15
+ public class ReturnInsnProcessorTest {
18
16
//the nodes in here need to be constructed by hand since Java compilers don't have this issue
19
-
20
17
@ 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 );
28
28
}
29
29
30
30
@ 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 );
32
34
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 );
33
38
}
34
39
40
+ @ Test
41
+ public void addsPopsForReferenceReturnType (){
42
+ int [] originalInsnSequence = {ACONST_NULL , DUP , DUP , DUP , ARETURN };
43
+ MethodNode mn = basicMethodFromSequence (Type .OBJECT , originalInsnSequence , 4 );
35
44
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 );
41
48
}
42
49
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
46
53
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 );
55
63
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 ;
67
66
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 );
69
70
}
70
71
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
+ }
71
80
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
+ }
72
91
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
+ }
73
101
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
+ }
74
117
}
0 commit comments