blob: f50116076b477454f3578f7e462f25aecbe04eb6 [file] [log] [blame]
/***
* 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();
}
}