1package test.javassist.bytecode.analysis;
2
3import java.io.IOException;
4
5import javassist.CannotCompileException;
6import javassist.ClassPool;
7import javassist.CtClass;
8import javassist.CtMethod;
9import javassist.NotFoundException;
10import javassist.bytecode.AccessFlag;
11import javassist.bytecode.Bytecode;
12import javassist.bytecode.MethodInfo;
13import javassist.bytecode.Opcode;
14import javassist.bytecode.analysis.Subroutine;
15import javassist.bytecode.analysis.SubroutineScanner;
16import junit.framework.TestCase;
17
18/**
19 * Tests Subroutine Scanner
20 *
21 * @author Jason T. Greene
22 */
23public class ScannerTest extends TestCase {
24
25    public void testNestedFinally() throws Exception {
26        ClassPool pool = ClassPool.getDefault();
27        generate(pool);
28        CtClass clazz = pool.get("test.ScannerTest$GeneratedTest");
29        CtMethod method = clazz.getDeclaredMethod("doit");
30
31        SubroutineScanner scanner = new SubroutineScanner();
32        Subroutine[] subs = scanner.scan(method.getMethodInfo2());
33
34        verifySubroutine(subs, 31, 31, new int[]{125, 25});
35        verifySubroutine(subs, 32, 31, new int[]{125, 25});
36        verifySubroutine(subs, 33, 31, new int[]{125, 25});
37        verifySubroutine(subs, 60, 31, new int[]{125, 25});
38        verifySubroutine(subs, 61, 31, new int[]{125, 25});
39        verifySubroutine(subs, 63, 31, new int[]{125, 25});
40        verifySubroutine(subs, 66, 31, new int[]{125, 25});
41        verifySubroutine(subs, 69, 31, new int[]{125, 25});
42        verifySubroutine(subs, 71, 31, new int[]{125, 25});
43        verifySubroutine(subs, 74, 31, new int[]{125, 25});
44        verifySubroutine(subs, 76, 31, new int[]{125, 25});
45        verifySubroutine(subs, 77, 77, new int[]{111, 71});
46        verifySubroutine(subs, 79, 77, new int[]{111, 71});
47        verifySubroutine(subs, 80, 77, new int[]{111, 71});
48        verifySubroutine(subs, 82, 77, new int[]{111, 71});
49        verifySubroutine(subs, 85, 77, new int[]{111, 71});
50        verifySubroutine(subs, 88, 77, new int[]{111, 71});
51        verifySubroutine(subs, 90, 77, new int[]{111, 71});
52        verifySubroutine(subs, 93, 77, new int[]{111, 71});
53        verifySubroutine(subs, 95, 77, new int[]{111, 71});
54        verifySubroutine(subs, 96, 96, new int[]{106, 90});
55        verifySubroutine(subs, 98, 96, new int[]{106, 90});
56        verifySubroutine(subs, 99, 96, new int[]{106, 90});
57        verifySubroutine(subs, 101, 96, new int[]{106, 90});
58        verifySubroutine(subs, 104, 96, new int[]{106, 90});
59        verifySubroutine(subs, 106, 77, new int[]{111, 71});
60        verifySubroutine(subs, 109, 77, new int[]{111, 71});
61        verifySubroutine(subs, 111, 31, new int[]{125, 25});
62        verifySubroutine(subs, 114, 31, new int[]{125, 25});
63        verifySubroutine(subs, 117, 31, new int[]{125, 25});
64        verifySubroutine(subs, 118, 31, new int[]{125, 25});
65        verifySubroutine(subs, 120, 31, new int[]{125, 25});
66        verifySubroutine(subs, 123, 31, new int[]{125, 25});
67    }
68
69    private static void verifySubroutine(Subroutine[] subs, int pos, int start,
70            int[] callers) {
71        Subroutine sub = subs[pos];
72        assertNotNull(sub);
73        assertEquals(sub.start(), start);
74        for (int i = 0; i < callers.length; i++)
75            assertTrue(sub.callers().contains(new Integer(callers[i])));
76    }
77
78    private static void generate(ClassPool pool) throws CannotCompileException, IOException, NotFoundException {
79        // Generated from eclipse JDK4 compiler:
80        // public void doit(int x) {
81        //    println("null");
82        //    try {
83        //        println("try");
84        //    } catch (RuntimeException e) {
85        //        e.printStackTrace();
86        //    } finally {
87        //        switch (x) {
88        //        default:
89        //        case 15:
90        //        try {
91        //            println("inner-try");
92        //        } finally {
93        //            try {
94        //                println("inner-inner-try");
95        //            } finally {
96        //                println("inner-finally");
97        //            }
98        //        }
99        //        break;
100        //        case 1789:
101        //        println("switch -17");
102        //        }
103        //    }
104        //}
105
106        CtClass clazz = pool.makeClass("test.ScannerTest$GeneratedTest");
107        CtMethod method = new CtMethod(CtClass.voidType, "doit", new CtClass[] {CtClass.intType}, clazz);
108        MethodInfo info = method.getMethodInfo2();
109        info.setAccessFlags(AccessFlag.PUBLIC);
110        CtClass stringClass = pool.get("java.lang.String");
111        Bytecode code = new Bytecode(info.getConstPool(), 2, 9);
112        /* 0   */ code.addAload(0);
113        /* 1   */ code.addLdc("start");
114        /* 3   */ code.addInvokevirtual(clazz, "println", CtClass.voidType, new CtClass[] {stringClass});
115        /* 6   */ code.addAload(0);
116        /* 7   */ code.addLdc("try");
117        /* 9   */ code.addInvokevirtual(clazz, "println", CtClass.voidType, new CtClass[] {stringClass});
118        /* 12  */ addJump(code, Opcode.GOTO, 125);
119        /* 14  */ code.addAstore(2);
120        /* 16  */ code.addAload(2);
121        /* 17  */ code.addInvokevirtual("java.lang.Exception", "printStackTrace", "()V");
122        /* 20  */ addJump(code, Opcode.GOTO, 125);
123        /* 23  */ code.addAstore(4);
124        /* 25  */ addJump(code, Opcode.JSR, 31);
125        /* 28  */ code.addAload(4);
126        /* 30  */ code.addOpcode(Opcode.ATHROW);
127        /* 31  */ code.addAstore(3);
128        /* 32  */ code.addIload(1);
129        int spos = code.currentPc();
130        /* 33  */ code.addOpcode(Opcode.LOOKUPSWITCH);
131                  code.addIndex(0); // 2 bytes pad - gets us to 36
132                  code.add32bit(60 - spos); // default
133                  code.add32bit(2); // 2 pairs
134                  code.add32bit(15); code.add32bit(60 - spos);
135                  code.add32bit(1789); code.add32bit(117 - spos);
136        /* 60  */ code.addAload(0);
137        /* 61  */ code.addLdc("inner-try");
138        /* 63  */ code.addInvokevirtual(clazz, "println", CtClass.voidType, new CtClass[] {stringClass});
139        /* 66  */ addJump(code, Opcode.GOTO, 111);
140        /* 69  */ code.addAstore(6);
141        /* 71  */ addJump(code, Opcode.JSR, 77);
142        /* 74  */ code.addAload(6);
143        /* 76  */ code.add(Opcode.ATHROW);
144        /* 77  */ code.addAstore(5);
145        /* 79  */ code.addAload(0);
146        /* 80  */ code.addLdc("inner-inner-try");
147        /* 82  */ code.addInvokevirtual(clazz, "println", CtClass.voidType, new CtClass[] {stringClass});
148        /* 85  */ addJump(code, Opcode.GOTO, 106);
149        /* 88  */ code.addAstore(8);
150        /* 90  */ addJump(code, Opcode.JSR, 96);
151        /* 93  */ code.addAload(8);
152        /* 95  */ code.add(Opcode.ATHROW);
153        /* 96  */ code.addAstore(7);
154        /* 98  */ code.addAload(0);
155        /* 99  */ code.addLdc("inner-finally");
156        /* 101 */ code.addInvokevirtual(clazz, "println", CtClass.voidType, new CtClass[] {stringClass});
157        /* 104 */ code.addRet(7);
158        /* 106 */ addJump(code, Opcode.JSR, 96);
159        /* 109 */ code.addRet(5);
160        /* 111 */ addJump(code, Opcode.JSR, 77);
161        /* 114 */ addJump(code, Opcode.GOTO, 123);
162        /* 117 */ code.addAload(0);
163        /* 118 */ code.addLdc("switch - 1789");
164        /* 120 */ code.addInvokevirtual(clazz, "println", CtClass.voidType, new CtClass[] {stringClass});
165        /* 123 */ code.addRet(3);
166        /* 125 */ addJump(code, Opcode.JSR, 31);
167        /* 128 */ code.addOpcode(Opcode.RETURN);
168        code.addExceptionHandler(6, 12, 15, "java.lang.RuntimeException");
169        code.addExceptionHandler(6, 20, 23, 0);
170        code.addExceptionHandler(125, 128, 23, 0);
171        code.addExceptionHandler(60, 69, 69, 0);
172        code.addExceptionHandler(111, 114, 69, 0);
173        code.addExceptionHandler(79, 88, 88, 0);
174        code.addExceptionHandler(106, 109, 88, 0);
175        info.setCodeAttribute(code.toCodeAttribute());
176        clazz.addMethod(method);
177        clazz.writeFile("/tmp");
178    }
179
180    private static void addJump(Bytecode code, int opcode, int pos) {
181        int current = code.currentPc();
182        code.addOpcode(opcode);
183        code.addIndex(pos - current);
184    }
185}
186