1package org.jf.dexlib2.builder;
2
3import com.google.common.collect.Lists;
4import junit.framework.Assert;
5import org.jf.dexlib2.Opcode;
6import org.jf.dexlib2.builder.instruction.BuilderInstruction10t;
7import org.jf.dexlib2.builder.instruction.BuilderInstruction10x;
8import org.jf.dexlib2.builder.instruction.BuilderInstruction20t;
9import org.jf.dexlib2.iface.MethodImplementation;
10import org.jf.dexlib2.iface.instruction.Instruction;
11import org.jf.dexlib2.iface.instruction.OffsetInstruction;
12import org.junit.Test;
13
14import java.util.List;
15
16public class FixGotoTest {
17    @Test
18    public void testFixGotoToGoto16() {
19        MethodImplementationBuilder builder = new MethodImplementationBuilder(1);
20
21        Label gotoTarget = builder.getLabel("gotoTarget");
22        builder.addInstruction(new BuilderInstruction10t(Opcode.GOTO, gotoTarget));
23
24        for (int i=0; i<500; i++) {
25            builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
26        }
27
28        builder.addLabel("gotoTarget");
29        builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID));
30
31        MethodImplementation impl = builder.getMethodImplementation();
32
33        List<? extends Instruction> instructions = Lists.newArrayList(impl.getInstructions());
34        Assert.assertEquals(502, instructions.size());
35
36        Assert.assertEquals(Opcode.GOTO_16, instructions.get(0).getOpcode());
37        Assert.assertEquals(502, ((OffsetInstruction)instructions.get(0)).getCodeOffset());
38    }
39
40    @Test
41    public void testFixGotoToGoto32() {
42        MethodImplementationBuilder builder = new MethodImplementationBuilder(1);
43
44        Label gotoTarget = builder.getLabel("gotoTarget");
45        builder.addInstruction(new BuilderInstruction10t(Opcode.GOTO, gotoTarget));
46
47        for (int i=0; i<70000; i++) {
48            builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
49        }
50
51        builder.addLabel("gotoTarget");
52        builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID));
53
54        MethodImplementation impl = builder.getMethodImplementation();
55
56        List<? extends Instruction> instructions = Lists.newArrayList(impl.getInstructions());
57        Assert.assertEquals(70002, instructions.size());
58
59        Assert.assertEquals(Opcode.GOTO_32, instructions.get(0).getOpcode());
60        Assert.assertEquals(70003, ((OffsetInstruction)instructions.get(0)).getCodeOffset());
61    }
62
63    @Test
64    public void testFixGoto16ToGoto32() {
65        MethodImplementationBuilder builder = new MethodImplementationBuilder(1);
66
67        Label gotoTarget = builder.getLabel("gotoTarget");
68        builder.addInstruction(new BuilderInstruction20t(Opcode.GOTO_16, gotoTarget));
69
70        for (int i=0; i<70000; i++) {
71            builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
72        }
73
74        builder.addLabel("gotoTarget");
75        builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID));
76
77        MethodImplementation impl = builder.getMethodImplementation();
78
79        List<? extends Instruction> instructions = Lists.newArrayList(impl.getInstructions());
80        Assert.assertEquals(70002, instructions.size());
81
82        Assert.assertEquals(Opcode.GOTO_32, instructions.get(0).getOpcode());
83        Assert.assertEquals(70003, ((OffsetInstruction)instructions.get(0)).getCodeOffset());
84    }
85
86    @Test
87    public void testFixGotoCascading() {
88        MethodImplementationBuilder builder = new MethodImplementationBuilder(1);
89
90        Label goto16Target = builder.getLabel("goto16Target");
91        builder.addInstruction(new BuilderInstruction20t(Opcode.GOTO_16, goto16Target));
92
93        for (int i=0; i<1000; i++) {
94            builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
95        }
96
97        Label gotoTarget = builder.getLabel("gotoTarget");
98        builder.addInstruction(new BuilderInstruction10t(Opcode.GOTO, gotoTarget));
99
100        for (int i=0; i<499; i++) {
101            builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
102        }
103
104        builder.addLabel("gotoTarget");
105
106        for (int i=0; i<31265; i++) {
107            builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
108        }
109
110        builder.addLabel("goto16Target");
111        builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID));
112
113        MethodImplementation impl = builder.getMethodImplementation();
114
115        List<? extends Instruction> instructions = Lists.newArrayList(impl.getInstructions());
116        Assert.assertEquals(32767, instructions.size());
117
118        Assert.assertEquals(Opcode.GOTO_32, instructions.get(0).getOpcode());
119        Assert.assertEquals(32769, ((OffsetInstruction)instructions.get(0)).getCodeOffset());
120
121    }
122}
123