/*** * ASM tests * Copyright (c) 2000-2011 INRIA, France Telecom * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ package org.objectweb.asm.commons; import junit.framework.TestCase; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.util.TraceMethodVisitor; import org.objectweb.asm.util.Textifier; /** * JsrInlinerTest * * @author Eugene Kuleshov, Niko Matsakis, Eric Bruneton */ public class JSRInlinerAdapterUnitTest extends TestCase { private JSRInlinerAdapter jsr; private MethodNode exp; private MethodVisitor current; @Override protected void setUp() throws Exception { super.setUp(); jsr = new JSRInlinerAdapter(null, 0, "m", "()V", null, null) { @Override public void visitEnd() { System.err.println("started w/ method:" + name); Textifier t = new Textifier(); TraceMethodVisitor mv = new TraceMethodVisitor(t); for (int i = 0; i < instructions.size(); ++i) { instructions.get(i).accept(mv); System.err.print(Integer.toString(i + 100000).substring(1)); System.err.print(" : " + t.text.get(i)); } super.visitEnd(); System.err.println("finished w/ method:" + name); } }; exp = new MethodNode(0, "m", "()V", null, null); } private void setCurrent(final MethodVisitor cv) { this.current = cv; } private void ICONST_0() { this.current.visitInsn(Opcodes.ICONST_0); } private void ISTORE(final int var) { this.current.visitVarInsn(Opcodes.ISTORE, var); } private void ALOAD(final int var) { this.current.visitVarInsn(Opcodes.ALOAD, var); } private void ILOAD(final int var) { this.current.visitVarInsn(Opcodes.ILOAD, var); } private void ASTORE(final int var) { this.current.visitVarInsn(Opcodes.ASTORE, var); } private void RET(final int var) { this.current.visitVarInsn(Opcodes.RET, var); } private void ATHROW() { this.current.visitInsn(Opcodes.ATHROW); } private void ACONST_NULL() { this.current.visitInsn(Opcodes.ACONST_NULL); } private void RETURN() { this.current.visitInsn(Opcodes.RETURN); } private void LABEL(final Label l) { this.current.visitLabel(l); } private void IINC(final int var, final int amnt) { this.current.visitIincInsn(var, amnt); } private void GOTO(final Label l) { this.current.visitJumpInsn(Opcodes.GOTO, l); } private void JSR(final Label l) { this.current.visitJumpInsn(Opcodes.JSR, l); } private void IFNONNULL(final Label l) { this.current.visitJumpInsn(Opcodes.IFNONNULL, l); } private void IFNE(final Label l) { this.current.visitJumpInsn(Opcodes.IFNE, l); } private void TRYCATCH(final Label start, final Label end, final Label handler) { this.current.visitTryCatchBlock(start, end, handler, null); } private void LINE(final int line, final Label start) { this.current.visitLineNumber(line, start); } private void LOCALVAR(final String name, final String desc, final int index, final Label start, final Label end) { this.current.visitLocalVariable(name, desc, null, start, end, index); } private void END(final int maxStack, final int maxLocals) { this.current.visitMaxs(maxStack, maxLocals); this.current.visitEnd(); ClassWriter cw = new ClassWriter(0); cw.visit(Opcodes.V1_1, Opcodes.ACC_PUBLIC, "C", null, "java/lang/Object", null); MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null); mv.visitCode(); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V"); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(1, 1); mv.visitEnd(); ((MethodNode) this.current).accept(cw); cw.visitEnd(); byte[] b = cw.toByteArray(); try { TestClassLoader loader = new TestClassLoader(); Class c = loader.defineClass("C", b); c.newInstance(); } catch (Throwable t) { fail(t.getMessage()); } this.current = null; } static class TestClassLoader extends ClassLoader { public Class defineClass(final String name, final byte[] b) { return defineClass(name, b, 0, b.length); } } /** * Tests a method which has the most basic try{}finally form * imaginable: * *
     * public void a() {
     *     int a = 0;
     *     try {
     *         a++;
     *     } finally {
     *         a--;
     *     }
     * }
     * 
*/ public void testBasic() { { Label L0 = new Label(); Label L1 = new Label(); Label L2 = new Label(); Label L3 = new Label(); Label L4 = new Label(); setCurrent(jsr); ICONST_0(); ISTORE(1); /* L0: body of try block */ LABEL(L0); IINC(1, 1); GOTO(L1); /* L2: exception handler */ LABEL(L2); ASTORE(3); JSR(L3); ALOAD(3); ATHROW(); /* L3: subroutine */ LABEL(L3); ASTORE(2); IINC(1, -1); RET(2); /* L1: non-exceptional exit from try block */ LABEL(L1); JSR(L3); LABEL(L4); // L4 RETURN(); TRYCATCH(L0, L2, L2); TRYCATCH(L1, L4, L2); END(1, 4); } { Label L0 = new Label(); Label L1 = new Label(); Label L2 = new Label(); Label L3_1a = new Label(); Label L3_1b = new Label(); Label L3_2a = new Label(); Label L3_2b = new Label(); Label L4 = new Label(); setCurrent(exp); ICONST_0(); ISTORE(1); // L0: try/catch block LABEL(L0); IINC(1, 1); GOTO(L1); // L2: Exception handler: LABEL(L2); ASTORE(3); ACONST_NULL(); GOTO(L3_1a); LABEL(L3_1b); // L3_1b; ALOAD(3); ATHROW(); // L1: On non-exceptional exit, try block leads here: LABEL(L1); ACONST_NULL(); GOTO(L3_2a); LABEL(L3_2b); // L3_2b LABEL(L4); // L4 RETURN(); // L3_1a: First instantiation of subroutine: LABEL(L3_1a); ASTORE(2); IINC(1, -1); GOTO(L3_1b); LABEL(new Label()); // extra label emitted due to impl quirks // L3_2a: Second instantiation of subroutine: LABEL(L3_2a); ASTORE(2); IINC(1, -1); GOTO(L3_2b); LABEL(new Label()); // extra label emitted due to impl quirks TRYCATCH(L0, L2, L2); TRYCATCH(L1, L4, L2); END(1, 4); } assertEquals(exp, jsr); } /** * Tests a method which has an if/else-if w/in the finally clause: * *
     * public void a() {
     *     int a = 0;
     *     try {
     *         a++;
     *     } finally {
     *         if (a == 0)
     *             a += 2;
     *         else
     *             a += 3;
     *     }
     * }
     * 
*/ public void testIfElseInFinally() { { Label L0 = new Label(); Label L1 = new Label(); Label L2 = new Label(); Label L3 = new Label(); Label L4 = new Label(); Label L5 = new Label(); Label L6 = new Label(); setCurrent(jsr); ICONST_0(); ISTORE(1); /* L0: body of try block */ LABEL(L0); IINC(1, 1); GOTO(L1); /* L2: exception handler */ LABEL(L2); ASTORE(3); JSR(L3); ALOAD(3); ATHROW(); /* L3: subroutine */ LABEL(L3); ASTORE(2); ILOAD(1); IFNE(L4); IINC(1, 2); GOTO(L5); LABEL(L4); // L4: a != 0 IINC(1, 3); LABEL(L5); // L5: common exit RET(2); /* L1: non-exceptional exit from try block */ LABEL(L1); JSR(L3); LABEL(L6); // L6 is used in the TRYCATCH below RETURN(); TRYCATCH(L0, L2, L2); TRYCATCH(L1, L6, L2); END(1, 4); } { Label L0 = new Label(); Label L1 = new Label(); Label L2 = new Label(); Label L3_1a = new Label(); Label L3_1b = new Label(); Label L3_2a = new Label(); Label L3_2b = new Label(); Label L4_1 = new Label(); Label L4_2 = new Label(); Label L5_1 = new Label(); Label L5_2 = new Label(); Label L6 = new Label(); setCurrent(exp); ICONST_0(); ISTORE(1); // L0: try/catch block LABEL(L0); IINC(1, 1); GOTO(L1); // L2: Exception handler: LABEL(L2); ASTORE(3); ACONST_NULL(); GOTO(L3_1a); LABEL(L3_1b); // L3_1b; ALOAD(3); ATHROW(); // L1: On non-exceptional exit, try block leads here: LABEL(L1); ACONST_NULL(); GOTO(L3_2a); LABEL(L3_2b); // L3_2b LABEL(L6); // L6 RETURN(); // L3_1a: First instantiation of subroutine: LABEL(L3_1a); ASTORE(2); ILOAD(1); IFNE(L4_1); IINC(1, 2); GOTO(L5_1); LABEL(L4_1); // L4_1: a != 0 IINC(1, 3); LABEL(L5_1); // L5_1: common exit GOTO(L3_1b); LABEL(new Label()); // extra label emitted due to impl quirks // L3_2a: First instantiation of subroutine: LABEL(L3_2a); ASTORE(2); ILOAD(1); IFNE(L4_2); IINC(1, 2); GOTO(L5_2); LABEL(L4_2); // L4_2: a != 0 IINC(1, 3); LABEL(L5_2); // L5_2: common exit GOTO(L3_2b); LABEL(new Label()); // extra label emitted due to impl quirks TRYCATCH(L0, L2, L2); TRYCATCH(L1, L6, L2); END(1, 4); } assertEquals(exp, jsr); } /** * Tests a simple nested finally: * *
     * public void a1() {
     *     int a = 0;
     *     try {
     *         a += 1;
     *     } finally {
     *         try {
     *             a += 2;
     *         } finally {
     *             a += 3;
     *         }
     *     }
     * }
     * 
*/ public void testSimpleNestedFinally() { { Label L0 = new Label(); Label L1 = new Label(); Label L2 = new Label(); Label L3 = new Label(); Label L4 = new Label(); Label L5 = new Label(); setCurrent(jsr); ICONST_0(); ISTORE(1); // L0: Body of try block: LABEL(L0); IINC(1, 1); JSR(L3); GOTO(L1); // L2: First exception handler: LABEL(L2); JSR(L3); ATHROW(); // L3: First subroutine: LABEL(L3); ASTORE(2); IINC(1, 2); JSR(L4); RET(2); // L5: Second exception handler: LABEL(L5); JSR(L4); ATHROW(); // L4: Second subroutine: LABEL(L4); ASTORE(3); IINC(1, 3); RET(3); // L1: On normal exit, try block jumps here: LABEL(L1); RETURN(); TRYCATCH(L0, L2, L2); TRYCATCH(L3, L5, L5); END(2, 6); } { Label L0 = new Label(); Label L1 = new Label(); Label L2 = new Label(); Label L3_1a = new Label(); Label L3_1b = new Label(); Label L3_2a = new Label(); Label L3_2b = new Label(); Label L4_1a = new Label(); Label L4_1b = new Label(); Label L4_2a = new Label(); Label L4_2b = new Label(); Label L4_3a = new Label(); Label L4_3b = new Label(); Label L4_4a = new Label(); Label L4_4b = new Label(); Label L5_1 = new Label(); Label L5_2 = new Label(); setCurrent(exp); ICONST_0(); ISTORE(1); // L0: Body of try block: LABEL(L0); IINC(1, 1); ACONST_NULL(); GOTO(L3_1a); LABEL(L3_1b); // L3_1b GOTO(L1); // L2: First exception handler: LABEL(L2); ACONST_NULL(); GOTO(L3_2a); LABEL(L3_2b); // L3_2b ATHROW(); // L1: On normal exit, try block jumps here: LABEL(L1); RETURN(); // L3_1a: First instantiation of first subroutine: LABEL(L3_1a); ASTORE(2); IINC(1, 2); ACONST_NULL(); GOTO(L4_1a); LABEL(L4_1b); // L4_1b GOTO(L3_1b); LABEL(L5_1); // L5_1 ACONST_NULL(); GOTO(L4_2a); LABEL(L4_2b); // L4_2b ATHROW(); LABEL(new Label()); // extra label emitted due to impl quirks // L3_2a: Second instantiation of first subroutine: LABEL(L3_2a); ASTORE(2); IINC(1, 2); ACONST_NULL(); GOTO(L4_3a); LABEL(L4_3b); // L4_3b GOTO(L3_2b); LABEL(L5_2); // L5_2 ACONST_NULL(); GOTO(L4_4a); LABEL(L4_4b); // L4_4b ATHROW(); LABEL(new Label()); // extra label emitted due to impl quirks // L4_1a: First instantiation of second subroutine: LABEL(L4_1a); ASTORE(3); IINC(1, 3); GOTO(L4_1b); LABEL(new Label()); // extra label emitted due to impl quirks // L4_2a: Second instantiation of second subroutine: LABEL(L4_2a); ASTORE(3); IINC(1, 3); GOTO(L4_2b); LABEL(new Label()); // extra label emitted due to impl quirks // L4_3a: Third instantiation of second subroutine: LABEL(L4_3a); ASTORE(3); IINC(1, 3); GOTO(L4_3b); LABEL(new Label()); // extra label emitted due to impl quirks // L4_4a: Fourth instantiation of second subroutine: LABEL(L4_4a); ASTORE(3); IINC(1, 3); GOTO(L4_4b); LABEL(new Label()); // extra label emitted due to impl quirks TRYCATCH(L0, L2, L2); TRYCATCH(L3_1a, L5_1, L5_1); TRYCATCH(L3_2a, L5_2, L5_2); END(2, 6); } assertEquals(exp, jsr); } /** * This tests a subroutine which has no ret statement, but ends in a * "return" instead. * * We structure this as a try/finally with a break in the finally. Because * the while loop is infinite, it's clear from the byte code that the only * path which reaches the RETURN instruction is through the subroutine. * *
     * public void a1() {
     *     int a = 0;
     *     while (true) {
     *         try {
     *             a += 1;
     *         } finally {
     *             a += 2;
     *             break;
     *         }
     *     }
     * }
     * 
*/ public void testSubroutineWithNoRet() { { Label L0 = new Label(); Label L1 = new Label(); Label L2 = new Label(); Label L3 = new Label(); Label L4 = new Label(); setCurrent(jsr); ICONST_0(); ISTORE(1); // L0: while loop header/try block LABEL(L0); IINC(1, 1); JSR(L1); GOTO(L2); // L3: implicit catch block LABEL(L3); ASTORE(2); JSR(L1); ALOAD(2); ATHROW(); // L1: subroutine ... LABEL(L1); ASTORE(3); IINC(1, 2); GOTO(L4); // ...not that it does not return! // L2: end of the loop... goes back to the top! LABEL(L2); GOTO(L0); // L4: LABEL(L4); RETURN(); TRYCATCH(L0, L3, L3); END(1, 4); } { Label L0 = new Label(); Label L1_1a = new Label(); Label L1_1b = new Label(); Label L1_2a = new Label(); Label L1_2b = new Label(); Label L2 = new Label(); Label L3 = new Label(); Label L4_1 = new Label(); Label L4_2 = new Label(); setCurrent(exp); ICONST_0(); ISTORE(1); // L0: while loop header/try block LABEL(L0); IINC(1, 1); ACONST_NULL(); GOTO(L1_1a); LABEL(L1_1b); // L1_1b GOTO(L2); // L3: implicit catch block LABEL(L3); ASTORE(2); ACONST_NULL(); GOTO(L1_2a); LABEL(L1_2b); // L1_2b ALOAD(2); ATHROW(); // L2: end of the loop... goes back to the top! LABEL(L2); GOTO(L0); LABEL(new Label()); // extra label emitted due to impl quirks // L1_1a: first instantiation of subroutine ... LABEL(L1_1a); ASTORE(3); IINC(1, 2); GOTO(L4_1); // ...not that it does not return! LABEL(L4_1); RETURN(); // L1_2a: second instantiation of subroutine ... LABEL(L1_2a); ASTORE(3); IINC(1, 2); GOTO(L4_2); // ...not that it does not return! LABEL(L4_2); RETURN(); TRYCATCH(L0, L3, L3); END(1, 4); } assertEquals(exp, jsr); } /** * This tests a subroutine which has no ret statement, but ends in a * "return" instead. * *
     *   JSR L0
     * L0:
     *   ASTORE 0
     *   RETURN
     * 
*/ public void testSubroutineWithNoRet2() { { Label L0 = new Label(); setCurrent(jsr); JSR(L0); LABEL(L0); ASTORE(0); RETURN(); END(1, 1); } { Label L0_1a = new Label(); Label L0_1b = new Label(); setCurrent(exp); ACONST_NULL(); GOTO(L0_1a); LABEL(L0_1b); // L0_1a: First instantiation of subroutine: LABEL(L0_1a); ASTORE(0); RETURN(); LABEL(new Label()); // extra label emitted due to impl quirks END(1, 1); } assertEquals(exp, jsr); } /** * This tests a subroutine which has no ret statement, but instead exits * implicitely by branching to code which is not part of the subroutine. * (Sadly, this is legal) * * We structure this as a try/finally in a loop with a break in the finally. * The loop is not trivially infinite, so the RETURN statement is reachable * both from the JSR subroutine and from the main entry point. * *
     * public void a1() {
     *     int a = 0;
     *     while (null == null) {
     *         try {
     *             a += 1;
     *         } finally {
     *             a += 2;
     *             break;
     *         }
     *     }
     * }
     * 
*/ public void testImplicitExit() { { Label L0 = new Label(); Label L1 = new Label(); Label L2 = new Label(); Label L3 = new Label(); Label L4 = new Label(); Label L5 = new Label(); setCurrent(jsr); ICONST_0(); ISTORE(1); // L5: while loop header LABEL(L5); ACONST_NULL(); IFNONNULL(L4); // L0: try block LABEL(L0); IINC(1, 1); JSR(L1); GOTO(L2); // L3: implicit catch block LABEL(L3); ASTORE(2); JSR(L1); ALOAD(2); ATHROW(); // L1: subroutine ... LABEL(L1); ASTORE(3); IINC(1, 2); GOTO(L4); // ...not that it does not return! // L2: end of the loop... goes back to the top! LABEL(L2); GOTO(L0); // L4: LABEL(L4); RETURN(); TRYCATCH(L0, L3, L3); END(1, 4); } { Label L0 = new Label(); Label L1_1a = new Label(); Label L1_1b = new Label(); Label L1_2a = new Label(); Label L1_2b = new Label(); Label L2 = new Label(); Label L3 = new Label(); Label L4 = new Label(); Label L5 = new Label(); setCurrent(exp); ICONST_0(); ISTORE(1); // L5: while loop header LABEL(L5); ACONST_NULL(); IFNONNULL(L4); // L0: while loop header/try block LABEL(L0); IINC(1, 1); ACONST_NULL(); GOTO(L1_1a); LABEL(L1_1b); // L1_1b GOTO(L2); // L3: implicit catch block LABEL(L3); ASTORE(2); ACONST_NULL(); GOTO(L1_2a); LABEL(L1_2b); // L1_2b ALOAD(2); ATHROW(); // L2: end of the loop... goes back to the top! LABEL(L2); GOTO(L0); // L4: exit, not part of subroutine // Note that the two subroutine instantiations branch here LABEL(L4); RETURN(); // L1_1a: first instantiation of subroutine ... LABEL(L1_1a); ASTORE(3); IINC(1, 2); GOTO(L4); // ...note that it does not return! LABEL(new Label()); // extra label emitted due to impl quirks // L1_2a: second instantiation of subroutine ... LABEL(L1_2a); ASTORE(3); IINC(1, 2); GOTO(L4); // ...note that it does not return! LABEL(new Label()); // extra label emitted due to impl quirks TRYCATCH(L0, L3, L3); END(1, 4); } assertEquals(exp, jsr); } /** * Tests a nested try/finally with implicit exit from one subroutine to the * other subroutine. Equivalent to the following java code: * *
     * void m(boolean b) {
     *     try {
     *         return;
     *     } finally {
     *         while (b) {
     *             try {
     *                 return;
     *             } finally {
     *                 // NOTE --- this break avoids the second return above (weird)
     *                 if (b)
     *                     break;
     *             }
     *         }
     *     }
     * }
     * 
* * This example is from the paper, "Subroutine Inlining and Bytecode * Abstraction to Simplify Static and Dynamic Analysis" by Cyrille Artho and * Armin Biere. */ public void testImplicitExitToAnotherSubroutine() { { Label T1 = new Label(); Label C1 = new Label(); Label S1 = new Label(); Label L = new Label(); Label C2 = new Label(); Label S2 = new Label(); Label W = new Label(); Label X = new Label(); // variable numbers: int b = 1; int e1 = 2; int e2 = 3; int r1 = 4; int r2 = 5; setCurrent(jsr); ICONST_0(); ISTORE(1); // T1: first try: LABEL(T1); JSR(S1); RETURN(); // C1: exception handler for first try LABEL(C1); ASTORE(e1); JSR(S1); ALOAD(e1); ATHROW(); // S1: first finally handler LABEL(S1); ASTORE(r1); GOTO(W); // L: body of while loop, also second try LABEL(L); JSR(S2); RETURN(); // C2: exception handler for second try LABEL(C2); ASTORE(e2); JSR(S2); ALOAD(e2); ATHROW(); // S2: second finally handler LABEL(S2); ASTORE(r2); ILOAD(b); IFNE(X); RET(r2); // W: test for the while loop LABEL(W); ILOAD(b); IFNE(L); // falls through to X // X: exit from finally{} block LABEL(X); RET(r1); TRYCATCH(T1, C1, C1); TRYCATCH(L, C2, C2); END(1, 6); } { Label T1 = new Label(); Label C1 = new Label(); Label S1_1a = new Label(); Label S1_1b = new Label(); Label S1_2a = new Label(); Label S1_2b = new Label(); Label L_1 = new Label(); Label L_2 = new Label(); Label C2_1 = new Label(); Label C2_2 = new Label(); Label S2_1_1a = new Label(); Label S2_1_1b = new Label(); Label S2_1_2a = new Label(); Label S2_1_2b = new Label(); Label S2_2_1a = new Label(); Label S2_2_1b = new Label(); Label S2_2_2a = new Label(); Label S2_2_2b = new Label(); Label W_1 = new Label(); Label W_2 = new Label(); Label X_1 = new Label(); Label X_2 = new Label(); // variable numbers: int b = 1; int e1 = 2; int e2 = 3; int r1 = 4; int r2 = 5; setCurrent(exp); // --- Main Subroutine --- ICONST_0(); ISTORE(1); // T1: first try: LABEL(T1); ACONST_NULL(); GOTO(S1_1a); LABEL(S1_1b); RETURN(); // C1: exception handler for first try LABEL(C1); ASTORE(e1); ACONST_NULL(); GOTO(S1_2a); LABEL(S1_2b); ALOAD(e1); ATHROW(); LABEL(new Label()); // extra label emitted due to impl quirks // --- First instantiation of first subroutine --- // S1: first finally handler LABEL(S1_1a); ASTORE(r1); GOTO(W_1); // L_1: body of while loop, also second try LABEL(L_1); ACONST_NULL(); GOTO(S2_1_1a); LABEL(S2_1_1b); RETURN(); // C2_1: exception handler for second try LABEL(C2_1); ASTORE(e2); ACONST_NULL(); GOTO(S2_1_2a); LABEL(S2_1_2b); ALOAD(e2); ATHROW(); // W_1: test for the while loop LABEL(W_1); ILOAD(b); IFNE(L_1); // falls through to X_1 // X_1: exit from finally{} block LABEL(X_1); GOTO(S1_1b); // --- Second instantiation of first subroutine --- // S1: first finally handler LABEL(S1_2a); ASTORE(r1); GOTO(W_2); // L_2: body of while loop, also second try LABEL(L_2); ACONST_NULL(); GOTO(S2_2_1a); LABEL(S2_2_1b); RETURN(); // C2_2: exception handler for second try LABEL(C2_2); ASTORE(e2); ACONST_NULL(); GOTO(S2_2_2a); LABEL(S2_2_2b); ALOAD(e2); ATHROW(); // W_2: test for the while loop LABEL(W_2); ILOAD(b); IFNE(L_2); // falls through to X_2 // X_2: exit from finally{} block LABEL(X_2); GOTO(S1_2b); // --- Second subroutine's 4 instantiations --- // S2_1_1a: LABEL(S2_1_1a); ASTORE(r2); ILOAD(b); IFNE(X_1); GOTO(S2_1_1b); LABEL(new Label()); // extra label emitted due to impl quirks // S2_1_2a: LABEL(S2_1_2a); ASTORE(r2); ILOAD(b); IFNE(X_1); GOTO(S2_1_2b); LABEL(new Label()); // extra label emitted due to impl quirks // S2_2_1a: LABEL(S2_2_1a); ASTORE(r2); ILOAD(b); IFNE(X_2); GOTO(S2_2_1b); LABEL(new Label()); // extra label emitted due to impl quirks // S2_2_2a: LABEL(S2_2_2a); ASTORE(r2); ILOAD(b); IFNE(X_2); GOTO(S2_2_2b); LABEL(new Label()); // extra label emitted due to impl quirks TRYCATCH(T1, C1, C1); TRYCATCH(L_1, C2_1, C2_1); // duplicated try/finally for each... TRYCATCH(L_2, C2_2, C2_2); // ...instantiation of first sub END(1, 6); } assertEquals(exp, jsr); } /** * This tests two subroutines, neither of which exit. Instead, they both * branch to a common set of code which returns from the method. This code * is not reachable except through these subroutines, and since they do not * invoke each other, it must be copied into both of them. * * I don't believe this can be represented in Java. */ public void testCommonCodeWhichMustBeDuplicated() { { Label L1 = new Label(); Label L2 = new Label(); Label L3 = new Label(); setCurrent(jsr); ICONST_0(); ISTORE(1); // Invoke the two subroutines, each twice: JSR(L1); JSR(L1); JSR(L2); JSR(L2); RETURN(); // L1: subroutine 1 LABEL(L1); IINC(1, 1); GOTO(L3); // ...note that it does not return! // L2: subroutine 2 LABEL(L2); IINC(1, 2); GOTO(L3); // ...note that it does not return! // L3: common code to both subroutines: exit method LABEL(L3); RETURN(); END(1, 2); } { Label L1_1a = new Label(); Label L1_1b = new Label(); Label L1_2a = new Label(); Label L1_2b = new Label(); Label L2_1a = new Label(); Label L2_1b = new Label(); Label L2_2a = new Label(); Label L2_2b = new Label(); Label L3_1 = new Label(); Label L3_2 = new Label(); Label L3_3 = new Label(); Label L3_4 = new Label(); setCurrent(exp); ICONST_0(); ISTORE(1); // Invoke the two subroutines, each twice: ACONST_NULL(); GOTO(L1_1a); LABEL(L1_1b); ACONST_NULL(); GOTO(L1_2a); LABEL(L1_2b); ACONST_NULL(); GOTO(L2_1a); LABEL(L2_1b); ACONST_NULL(); GOTO(L2_2a); LABEL(L2_2b); RETURN(); LABEL(new Label()); // extra label emitted due to impl quirks // L1_1a: instantiation 1 of subroutine 1 LABEL(L1_1a); IINC(1, 1); GOTO(L3_1); // ...note that it does not return! LABEL(L3_1); RETURN(); // L1_2a: instantiation 2 of subroutine 1 LABEL(L1_2a); IINC(1, 1); GOTO(L3_2); // ...note that it does not return! LABEL(L3_2); RETURN(); // L2_1a: instantiation 1 of subroutine 2 LABEL(L2_1a); IINC(1, 2); GOTO(L3_3); // ...note that it does not return! LABEL(L3_3); RETURN(); // L2_2a: instantiation 2 of subroutine 2 LABEL(L2_2a); IINC(1, 2); GOTO(L3_4); // ...note that it does not return! LABEL(L3_4); RETURN(); END(1, 2); } assertEquals(exp, jsr); } /** * This tests a simple subroutine where the control flow jumps back and * forth between the subroutine and the caller. * * This would not normally be produced by a java compiler. */ public void testInterleavedCode() { { Label L1 = new Label(); Label L2 = new Label(); Label L3 = new Label(); Label L4 = new Label(); setCurrent(jsr); ICONST_0(); ISTORE(1); // Invoke the subroutine, each twice: JSR(L1); GOTO(L2); // L1: subroutine 1 LABEL(L1); ASTORE(2); IINC(1, 1); GOTO(L3); // L2: second part of main subroutine LABEL(L2); IINC(1, 2); GOTO(L4); // L3: second part of subroutine 1 LABEL(L3); IINC(1, 4); RET(2); // L4: third part of main subroutine LABEL(L4); JSR(L1); RETURN(); END(1, 3); } { Label L1_1a = new Label(); Label L1_1b = new Label(); Label L1_2a = new Label(); Label L1_2b = new Label(); Label L2 = new Label(); Label L3_1 = new Label(); Label L3_2 = new Label(); Label L4 = new Label(); setCurrent(exp); // Main routine: ICONST_0(); ISTORE(1); ACONST_NULL(); GOTO(L1_1a); LABEL(L1_1b); GOTO(L2); LABEL(L2); IINC(1, 2); GOTO(L4); LABEL(L4); ACONST_NULL(); GOTO(L1_2a); LABEL(L1_2b); RETURN(); // L1_1: instantiation #1 LABEL(L1_1a); ASTORE(2); IINC(1, 1); GOTO(L3_1); LABEL(L3_1); IINC(1, 4); GOTO(L1_1b); LABEL(new Label()); // extra label emitted due to impl quirks // L1_2: instantiation #2 LABEL(L1_2a); ASTORE(2); IINC(1, 1); GOTO(L3_2); LABEL(L3_2); IINC(1, 4); GOTO(L1_2b); LABEL(new Label()); // extra label emitted due to impl quirks END(1, 3); } assertEquals(exp, jsr); } /** * Tests a nested try/finally with implicit exit from one subroutine to the * other subroutine, and with a surrounding try/catch thrown in the mix. * Equivalent to the following java code: * *
     * void m(int b) {
     *     try {
     *         try {
     *             return;
     *         } finally {
     *             while (b) {
     *                 try {
     *                     return;
     *                 } finally {
     *                     // NOTE --- this break avoids the second return above
     *                     // (weird)
     *                     if (b)
     *                         break;
     *                 }
     *             }
     *         }
     *     } catch (Exception e) {
     *         b += 3;
     *         return;
     *     }
     * }
     * 
*/ public void testImplicitExitInTryCatch() { { Label T1 = new Label(); Label C1 = new Label(); Label S1 = new Label(); Label L = new Label(); Label C2 = new Label(); Label S2 = new Label(); Label W = new Label(); Label X = new Label(); Label OT = new Label(); Label OC = new Label(); // variable numbers: int b = 1; int e1 = 2; int e2 = 3; int r1 = 4; int r2 = 5; setCurrent(jsr); ICONST_0(); ISTORE(1); // OT: outermost try LABEL(OT); // T1: first try: LABEL(T1); JSR(S1); RETURN(); // C1: exception handler for first try LABEL(C1); ASTORE(e1); JSR(S1); ALOAD(e1); ATHROW(); // S1: first finally handler LABEL(S1); ASTORE(r1); GOTO(W); // L: body of while loop, also second try LABEL(L); JSR(S2); RETURN(); // C2: exception handler for second try LABEL(C2); ASTORE(e2); JSR(S2); ALOAD(e2); ATHROW(); // S2: second finally handler LABEL(S2); ASTORE(r2); ILOAD(b); IFNE(X); RET(r2); // W: test for the while loop LABEL(W); ILOAD(b); IFNE(L); // falls through to X // X: exit from finally{} block LABEL(X); RET(r1); // OC: outermost catch LABEL(OC); IINC(b, 3); RETURN(); TRYCATCH(T1, C1, C1); TRYCATCH(L, C2, C2); TRYCATCH(OT, OC, OC); END(1, 6); } { Label T1 = new Label(); Label C1 = new Label(); Label S1_1a = new Label(); Label S1_1b = new Label(); Label S1_2a = new Label(); Label S1_2b = new Label(); Label L_1 = new Label(); Label L_2 = new Label(); Label C2_1 = new Label(); Label C2_2 = new Label(); Label S2_1_1a = new Label(); Label S2_1_1b = new Label(); Label S2_1_2a = new Label(); Label S2_1_2b = new Label(); Label S2_2_1a = new Label(); Label S2_2_1b = new Label(); Label S2_2_2a = new Label(); Label S2_2_2b = new Label(); Label W_1 = new Label(); Label W_2 = new Label(); Label X_1 = new Label(); Label X_2 = new Label(); Label OT_1 = S1_1a; Label OT_2 = S1_2a; Label OT_1_1 = S2_1_1a; Label OT_1_2 = S2_1_2a; Label OT_2_1 = S2_2_1a; Label OT_2_2 = S2_2_2a; Label OC = new Label(); Label OC_1 = new Label(); Label OC_2 = new Label(); Label OC_1_1 = new Label(); Label OC_1_2 = new Label(); Label OC_2_1 = new Label(); Label OC_2_2 = new Label(); // variable numbers: int b = 1; int e1 = 2; int e2 = 3; int r1 = 4; int r2 = 5; setCurrent(exp); // --- Main Subroutine --- ICONST_0(); ISTORE(1); // T1: outermost try / first try: LABEL(T1); ACONST_NULL(); GOTO(S1_1a); LABEL(S1_1b); RETURN(); // C1: exception handler for first try LABEL(C1); ASTORE(e1); ACONST_NULL(); GOTO(S1_2a); LABEL(S1_2b); ALOAD(e1); ATHROW(); // OC: Outermost catch LABEL(OC); IINC(b, 3); RETURN(); // --- First instantiation of first subroutine --- // S1: first finally handler LABEL(S1_1a); ASTORE(r1); GOTO(W_1); // L_1: body of while loop, also second try LABEL(L_1); ACONST_NULL(); GOTO(S2_1_1a); LABEL(S2_1_1b); RETURN(); // C2_1: exception handler for second try LABEL(C2_1); ASTORE(e2); ACONST_NULL(); GOTO(S2_1_2a); LABEL(S2_1_2b); ALOAD(e2); ATHROW(); // W_1: test for the while loop LABEL(W_1); ILOAD(b); IFNE(L_1); // falls through to X_1 // X_1: exit from finally{} block LABEL(X_1); GOTO(S1_1b); LABEL(OC_1); // --- Second instantiation of first subroutine --- // S1: first finally handler LABEL(S1_2a); ASTORE(r1); GOTO(W_2); // L_2: body of while loop, also second try LABEL(L_2); ACONST_NULL(); GOTO(S2_2_1a); LABEL(S2_2_1b); RETURN(); // C2_2: exception handler for second try LABEL(C2_2); ASTORE(e2); ACONST_NULL(); GOTO(S2_2_2a); LABEL(S2_2_2b); ALOAD(e2); ATHROW(); // W_2: test for the while loop LABEL(W_2); ILOAD(b); IFNE(L_2); // falls through to X_2 // X_2: exit from finally{} block LABEL(X_2); GOTO(S1_2b); LABEL(OC_2); // --- Second subroutine's 4 instantiations --- // S2_1_1a: LABEL(S2_1_1a); ASTORE(r2); ILOAD(b); IFNE(X_1); GOTO(S2_1_1b); LABEL(OC_1_1); // S2_1_2a: LABEL(S2_1_2a); ASTORE(r2); ILOAD(b); IFNE(X_1); GOTO(S2_1_2b); LABEL(OC_1_2); // S2_2_1a: LABEL(S2_2_1a); ASTORE(r2); ILOAD(b); IFNE(X_2); GOTO(S2_2_1b); LABEL(OC_2_1); // S2_2_2a: LABEL(S2_2_2a); ASTORE(r2); ILOAD(b); IFNE(X_2); GOTO(S2_2_2b); LABEL(OC_2_2); // main subroutine handlers: TRYCATCH(T1, C1, C1); TRYCATCH(T1, OC, OC); // first instance of first sub try/catch handlers: TRYCATCH(L_1, C2_1, C2_1); TRYCATCH(OT_1, OC_1, OC); // note: reuses handler code from main // sub // second instance of first sub try/catch handlers: TRYCATCH(L_2, C2_2, C2_2); TRYCATCH(OT_2, OC_2, OC); // all 4 instances of second sub: TRYCATCH(OT_1_1, OC_1_1, OC); TRYCATCH(OT_1_2, OC_1_2, OC); TRYCATCH(OT_2_1, OC_2_1, OC); TRYCATCH(OT_2_2, OC_2_2, OC); END(1, 6); } assertEquals(exp, jsr); } /** * Tests a method which has line numbers and local variable declarations. * *
     *   public void a() {
     * 1    int a = 0;
     * 2    try {
     * 3      a++;
     * 4    } finally {
     * 5      a--;
     * 6    }
     *   }
     *   LV "a" from 1 to 6
     * 
*/ public void testBasicLineNumberAndLocalVars() { { Label LM1 = new Label(); Label L0 = new Label(); Label L1 = new Label(); Label L2 = new Label(); Label L3 = new Label(); Label L4 = new Label(); setCurrent(jsr); LABEL(LM1); LINE(1, LM1); ICONST_0(); ISTORE(1); /* L0: body of try block */ LABEL(L0); LINE(3, L0); IINC(1, 1); GOTO(L1); /* L2: exception handler */ LABEL(L2); ASTORE(3); JSR(L3); ALOAD(3); ATHROW(); /* L3: subroutine */ LABEL(L3); LINE(5, L3); ASTORE(2); IINC(1, -1); RET(2); /* L1: non-exceptional exit from try block */ LABEL(L1); JSR(L3); LABEL(L4); // L4 RETURN(); TRYCATCH(L0, L2, L2); TRYCATCH(L1, L4, L2); LOCALVAR("a", "I", 1, LM1, L4); END(1, 4); } { Label LM1 = new Label(); Label L0 = new Label(); Label L1 = new Label(); Label L2 = new Label(); Label L3_1a = new Label(); Label L3_1b = new Label(); Label L3_1c = new Label(); Label L3_2a = new Label(); Label L3_2b = new Label(); Label L3_2c = new Label(); Label L4 = new Label(); setCurrent(exp); LABEL(LM1); LINE(1, LM1); ICONST_0(); ISTORE(1); // L0: try/catch block LABEL(L0); LINE(3, L0); IINC(1, 1); GOTO(L1); // L2: Exception handler: LABEL(L2); ASTORE(3); ACONST_NULL(); GOTO(L3_1a); LABEL(L3_1b); // L3_1b; ALOAD(3); ATHROW(); // L1: On non-exceptional exit, try block leads here: LABEL(L1); ACONST_NULL(); GOTO(L3_2a); LABEL(L3_2b); // L3_2b LABEL(L4); // L4 RETURN(); // L3_1a: First instantiation of subroutine: LABEL(L3_1a); LINE(5, L3_1a); ASTORE(2); IINC(1, -1); GOTO(L3_1b); LABEL(L3_1c); // L3_2a: Second instantiation of subroutine: LABEL(L3_2a); LINE(5, L3_2a); ASTORE(2); IINC(1, -1); GOTO(L3_2b); LABEL(L3_2c); TRYCATCH(L0, L2, L2); TRYCATCH(L1, L4, L2); LOCALVAR("a", "I", 1, LM1, L4); LOCALVAR("a", "I", 1, L3_1a, L3_1c); LOCALVAR("a", "I", 1, L3_2a, L3_2c); END(1, 4); } assertEquals(exp, jsr); } public void assertEquals(final MethodNode exp, final MethodNode actual) { String textexp = getText(exp); String textact = getText(actual); System.err.println("Expected=" + textexp); System.err.println("Actual=" + textact); assertEquals(textexp, textact); } private String getText(final MethodNode mn) { Textifier tv = new Textifier(); TraceMethodVisitor tmv = new TraceMethodVisitor(tv); mn.accept(tmv); StringBuffer sb = new StringBuffer(); for (int i = 0; i < tv.text.size(); i++) { sb.append(tv.text.get(i)); } return sb.toString(); } }