| /*** |
| * 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, "<init>", "()V", |
| null, null); |
| mv.visitCode(); |
| mv.visitVarInsn(Opcodes.ALOAD, 0); |
| mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", |
| "()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 <code>try{}finally</code> form |
| * imaginable: |
| * |
| * <pre> |
| * public void a() { |
| * int a = 0; |
| * try { |
| * a++; |
| * } finally { |
| * a--; |
| * } |
| * } |
| * </pre> |
| */ |
| 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: |
| * |
| * <pre> |
| * public void a() { |
| * int a = 0; |
| * try { |
| * a++; |
| * } finally { |
| * if (a == 0) |
| * a += 2; |
| * else |
| * a += 3; |
| * } |
| * } |
| * </pre> |
| */ |
| 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: |
| * |
| * <pre> |
| * public void a1() { |
| * int a = 0; |
| * try { |
| * a += 1; |
| * } finally { |
| * try { |
| * a += 2; |
| * } finally { |
| * a += 3; |
| * } |
| * } |
| * } |
| * </pre> |
| */ |
| 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. |
| * |
| * <pre> |
| * public void a1() { |
| * int a = 0; |
| * while (true) { |
| * try { |
| * a += 1; |
| * } finally { |
| * a += 2; |
| * break; |
| * } |
| * } |
| * } |
| * </pre> |
| */ |
| 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. |
| * |
| * <pre> |
| * JSR L0 |
| * L0: |
| * ASTORE 0 |
| * RETURN |
| * </pre> |
| */ |
| 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. |
| * |
| * <pre> |
| * public void a1() { |
| * int a = 0; |
| * while (null == null) { |
| * try { |
| * a += 1; |
| * } finally { |
| * a += 2; |
| * break; |
| * } |
| * } |
| * } |
| * </pre> |
| */ |
| 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: |
| * |
| * <pre> |
| * void m(boolean b) { |
| * try { |
| * return; |
| * } finally { |
| * while (b) { |
| * try { |
| * return; |
| * } finally { |
| * // NOTE --- this break avoids the second return above (weird) |
| * if (b) |
| * break; |
| * } |
| * } |
| * } |
| * } |
| * </pre> |
| * |
| * 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: |
| * |
| * <pre> |
| * 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; |
| * } |
| * } |
| * </pre> |
| */ |
| 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. |
| * |
| * <pre> |
| * public void a() { |
| * 1 int a = 0; |
| * 2 try { |
| * 3 a++; |
| * 4 } finally { |
| * 5 a--; |
| * 6 } |
| * } |
| * LV "a" from 1 to 6 |
| * </pre> |
| */ |
| 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(); |
| } |
| } |