blob: f215af3c6e765a3614a6e91b75f25c9bd210e0fb [file] [log] [blame]
// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.jasmin;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.jasmin.JasminBuilder.ClassFileVersion;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import org.junit.Test;
public class JumpSubroutineTests extends JasminTestBase {
private void runTest(JasminBuilder builder, String main, String expected) throws Exception {
String javaResult = runOnJava(builder, main);
assertEquals(expected, javaResult);
String artResult = runOnArtD8(builder, main);
assertEquals(expected, artResult);
String cfFrontendResult = runOnJavaR8CfFrontend(builder, main);
assertEquals(expected, cfFrontendResult);
String dxArtResult = runOnArtDx(builder, main);
assertEquals(expected, dxArtResult);
}
private String runOnJavaR8CfFrontend(JasminBuilder builder, String main) throws Exception {
Path inputJar = temp.getRoot().toPath().resolve("input.jar");
Path outputJar = temp.getRoot().toPath().resolve("output.jar");
builder.writeJar(inputJar, null);
ToolHelper.runR8(
R8Command.builder()
.addProgramFiles(inputJar)
.setOutput(outputJar, OutputMode.ClassFile)
.addLibraryFiles(ToolHelper.getJava8RuntimeJar())
.setDisableTreeShaking(true)
.setDisableMinification(true)
.addProguardConfiguration(ImmutableList.of("-keepattributes *"), Origin.unknown())
.build());
ProcessResult processResult = ToolHelper.runJava(outputJar, main);
assertEquals(0, processResult.exitCode);
return processResult.stdout;
}
private void expectDxFailure(JasminBuilder builder) throws Exception {
// This expects this dx failure:
// Uncaught translation error: com.android.dex.util.ExceptionWithContext: returning from
// invalid subroutine
// 1 error; aborting
ProcessResult result = runDX(builder);
assertNotEquals(0, result.exitCode);
assertTrue(result.stderr.contains("Uncaught translation error"));
assertTrue(result.stderr.contains("invalid subroutine"));
}
@Test
/*
* Compilation of the following code with JDK 1.3.0_05 (on Windows).
*
* package test;
*
* class Test {
* public static void main(String[] args) {
* try {
* System.out.println(0);
* } finally {
* System.out.println(2);
* }
* }
* }
*/
public void testJsrJava130TryFinally() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_3);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addMainMethod(
".limit stack 3",
".limit locals 3",
"TryStart:",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" iconst_0",
" invokevirtual java/io/PrintStream/print(I)V",
" jsr Finally",
" goto Return",
"TryEnd:",
"Catch:",
" astore_1",
" jsr Finally",
" aload_1",
" athrow",
"Finally:",
" astore_2",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" iconst_1",
" invokevirtual java/io/PrintStream/print(I)V",
" ret 2",
"Return:",
" return",
".catch all from TryStart to TryEnd using Catch");
runTest(builder, clazz.name, "01");
}
@Test
/*
* Compilation of the following code with JDK 1.3.0_05 (on Windows).
*
* package test;
*
* class Test {
* public static void main(String[] args) {
* try {
* System.out.println(0);
* try {
* System.out.println(1);
* } finally {
* System.out.println(2);
* }
* } finally {
* System.out.println(3);
* }
* }
* }
*/
public void testJsrJava130TryFinallyNested() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_3);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addMainMethod(
".limit stack 3",
".limit locals 5",
"TryStart:",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" iconst_0",
" invokevirtual java/io/PrintStream/print(I)V",
"TryStartInner:",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" iconst_1",
" invokevirtual java/io/PrintStream/print(I)V",
" jsr FinallyInner",
" goto DoneInner",
"TryEndInner:",
"CatchInner:",
" astore_1",
" jsr FinallyInner",
" aload_1",
" athrow",
"FinallyInner:",
" astore_2",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" iconst_2",
" invokevirtual java/io/PrintStream/print(I)V",
" ret 2",
"DoneInner:",
" jsr Finally",
" goto Return",
"TryEnd:",
"Catch:",
" astore_3",
" jsr Finally",
" aload_3",
" athrow",
"Finally:",
" astore 4",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" iconst_3",
" invokevirtual java/io/PrintStream/print(I)V",
" ret 4",
"Return:",
" return",
".catch all from TryStartInner to TryEndInner using CatchInner",
".catch all from TryStart to TryEnd using Catch");
runTest(builder, clazz.name, "0123");
}
@Test
public void testJsrWithStraightlineCode() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addStaticMethod("foo", ImmutableList.of(), "I",
".limit stack 3",
".limit locals 3",
" ldc 0",
" ldc 1",
" jsr LabelSub",
" ldc 2",
" jsr LabelSub",
" ldc 3",
" jsr LabelSub",
" ireturn",
"LabelSub:",
" astore 1",
" iadd",
" ret 1");
clazz.addMainMethod(
".limit stack 2",
".limit locals 1",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" invokestatic Test/foo()I",
" invokevirtual java/io/PrintStream/print(I)V",
" return");
runTest(builder, clazz.name, Integer.toString(3 * 4 / 2));
}
@Test
public void testJsrWithStraightlineCodeMultiple() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addStaticMethod("foo", ImmutableList.of(), "I",
".limit stack 3",
".limit locals 3",
" ldc 0",
" ldc 1",
" jsr LabelSub1",
" ldc 2",
" jsr LabelSub2",
" ldc 3",
" jsr LabelSub3",
" ireturn",
"LabelSub1:",
" astore 1",
" iadd",
" ret 1",
"LabelSub2:",
" astore 1",
" iadd",
" ret 1",
"LabelSub3:",
" astore 1",
" iadd",
" ret 1");
clazz.addMainMethod(
".limit stack 2",
".limit locals 1",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" invokestatic Test/foo()I",
" invokevirtual java/io/PrintStream/print(I)V",
" return");
runTest(builder, clazz.name, Integer.toString(3 * 4 / 2));
}
@Test
public void testJsrWithStraightlineCodeMultiple2() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addStaticMethod("foo", ImmutableList.of(), "I",
".limit stack 4",
".limit locals 3",
" ldc 0",
" ldc 1",
" jsr LabelSub",
" ldc 2",
" jsr LabelSub",
" ldc 3",
" jsr LabelSub",
" ldc 4",
" ldc 5",
" jsr LabelSub2",
" ldc 6",
" ldc 7",
" jsr LabelSub2",
" ldc 8",
" ldc 9",
" jsr LabelSub2",
" ireturn",
"LabelSub:",
" astore 1",
" iadd",
" ret 1",
"LabelSub2:",
" astore 1",
" iadd",
" iadd",
" ret 1");
clazz.addMainMethod(
".limit stack 2",
".limit locals 1",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" invokestatic Test/foo()I",
" invokevirtual java/io/PrintStream/print(I)V",
" return");
runTest(builder, clazz.name, Integer.toString(9 * 10 / 2));
}
@Test
public void testJsrWithControlFlowCode() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addStaticMethod("foo", ImmutableList.of(), "V",
".limit stack 2",
".limit locals 2",
" ldc 0",
" jsr LabelSub",
" ldc 1",
" jsr LabelSub",
" return",
"LabelSub:",
" astore 1",
" ifeq LabelZero",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc \"Got non-zero\"",
" invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V",
" goto LabelRet",
"LabelZero:",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc \"Got zero\"",
" invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V",
"LabelRet:",
" ret 1");
clazz.addMainMethod(
".limit stack 0",
".limit locals 1",
" invokestatic Test/foo()V",
" return");
runTest(builder, clazz.name, "Got zero" + ToolHelper.LINE_SEPARATOR + "Got non-zero"
+ ToolHelper.LINE_SEPARATOR);
}
@Test
public void testJsrWithNestedJsr() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addStaticMethod("foo", ImmutableList.of(), "V",
".limit stack 2",
".limit locals 3",
" ldc 0",
" jsr LabelSub", // index 1.
" ldc 1",
" jsr LabelSub", // index 3.
" return",
"LabelSub:",
" astore 1",
" ifeq LabelZero",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc \"Got non-zero, calling nested\"",
" invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V",
" jsr LabelSub2", // index 11.
" goto LabelRet",
"LabelZero:",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc \"Got zero\"",
" invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V",
"LabelRet:",
" ret 1",
"LabelSub2:",
" astore 2",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc \"In nested subroutine\"",
" invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V",
" ret 2");
clazz.addMainMethod(
".limit stack 0",
".limit locals 1",
" invokestatic Test/foo()V",
" return");
runTest(builder, clazz.name, "Got zero" + ToolHelper.LINE_SEPARATOR
+ "Got non-zero, calling nested" + ToolHelper.LINE_SEPARATOR + "In nested subroutine"
+ ToolHelper.LINE_SEPARATOR);
}
@Test
public void testJsrWithNestedJsrPopReturnAddress() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addStaticMethod("foo", ImmutableList.of(), "V",
".limit stack 1",
".limit locals 1",
" jsr LabelSub1",
" return",
"LabelSub1:",
" astore 0",
" jsr LabelSub2",
"LabelSub2:",
" pop",
" ret 0");
clazz.addMainMethod(
".limit stack 1",
".limit locals 1",
" invokestatic Test/foo()V",
" return");
String expected = "";
String javaResult = runOnJava(builder, clazz.name);
assertEquals(expected, javaResult);
String artResult = runOnArtD8(builder, clazz.name);
assertEquals(expected, artResult);
String cfFrontendResult = runOnJavaR8CfFrontend(builder, clazz.name);
assertEquals(expected, cfFrontendResult);
// This fails with dx.
expectDxFailure(builder);
}
@Test
public void testJsrWithNestedPopReturnAddress2() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addStaticMethod("foo", ImmutableList.of(), "V",
".limit stack 1",
".limit locals 1",
" jsr LabelSub1",
"LabelSub1:",
" pop",
" jsr LabelSub2",
" return",
"LabelSub2:",
" astore 0",
" ret 0");
clazz.addMainMethod(
".limit stack 1",
".limit locals 1",
" invokestatic Test/foo()V",
" return");
runTest(builder, clazz.name, "");
}
@Test
public void testJsrJustThrows() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addStaticMethod("foo", ImmutableList.of(), "V",
".limit stack 2",
".limit locals 1",
" jsr Jsr",
// The return target is the same as the jsr target.
"Jsr:",
" pop", // Return address is not used.
" new java/lang/Exception",
" dup",
" invokenonvirtual java/lang/Exception.<init>()V",
" athrow");
clazz.addMainMethod(
".limit stack 1",
".limit locals 1",
"TryStart:",
" invokestatic Test/foo()V",
" return",
"TryEnd:",
"Catch:",
" pop",
" return",
".catch java/lang/Exception from TryStart to TryEnd using Catch");
runTest(builder, clazz.name, "");
}
@Test
public void testJsrJustThrows2() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addStaticMethod("foo", ImmutableList.of(), "V",
".limit stack 2",
".limit locals 1",
" jsr Jsr",
// The jsr does not return, so this is dead code.
" pop",
" pop",
" pop",
" pop",
" pop",
" pop",
" return",
"Jsr:",
" pop", // Return address is not used.
" new java/lang/Exception",
" dup",
" invokenonvirtual java/lang/Exception.<init>()V",
" athrow");
clazz.addMainMethod(
".limit stack 1",
".limit locals 1",
"TryStart:",
" invokestatic Test/foo()V",
" return",
"TryEnd:",
"Catch:",
" pop",
" return",
".catch java/lang/Exception from TryStart to TryEnd using Catch");
runTest(builder, clazz.name, "");
}
@Test
public void testJsrWithException() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addStaticMethod("foo", ImmutableList.of(), "V",
".limit stack 3",
".limit locals 2",
" ldc 0",
" jsr LabelSub",
" ldc 1",
" jsr LabelSub",
" return",
"LabelSub:",
" astore 1",
" ldc 42",
" swap",
" ldc 42",
" swap",
"LabelTryStart:",
" idiv",
" pop",
"LabelTryEnd:",
" pop",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc \"Divided by non-zero\"",
" invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V",
" goto LabelRet",
"LabelCatch:",
" pop",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc \"Divided by zero\"",
" invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V",
" goto LabelRet",
"LabelRet:",
" ret 1",
".catch java/lang/Exception from LabelTryStart to LabelTryEnd using LabelCatch");
clazz.addMainMethod(
".limit stack 0",
".limit locals 1",
" invokestatic Test/foo()V",
" return");
runTest(builder, clazz.name, "Divided by zero" + ToolHelper.LINE_SEPARATOR
+ "Divided by non-zero" + ToolHelper.LINE_SEPARATOR);
}
@Test
public void testJsrWithAddressManipulation() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addStaticMethod("foo", ImmutableList.of(), "V",
".limit stack 4",
".limit locals 4",
" ldc 0",
" jsr LabelSub",
" ldc 1",
" jsr LabelSub",
" return",
"LabelSub:",
" ldc \"junk\"",
" swap",
" dup",
// stack is now ..., arg, "junk", addr, addr
" astore 3",
" astore 2",
" astore 1",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" swap",
" invokevirtual java/io/PrintStream/print(I)V",
" ret 2");
clazz.addMainMethod(
".limit stack 0",
".limit locals 1",
" invokestatic Test/foo()V",
" return");
runTest(builder, clazz.name, "01");
}
@Test
public void testJsrWithSharedExceptionHandler() throws Exception {
// Regression test for b/37659886
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addStaticMethod("foo", ImmutableList.of("I"), "V",
".limit stack 4",
".limit locals 2",
"LabelTryStart:",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" iload 0",
" jsr LabelSub",
" invokevirtual java/io/PrintStream/print(I)V",
" return",
"LabelSub:",
" astore 1",
" dup",
" ldc 168",
" swap",
" idiv", // First throwing shares the handler with JSR.
"LabelTryEnd1:",
" swap",
" idiv", // Second throwing is in the outer handler, but still opened at the point of JSR.
" ret 1",
"LabelTryEnd2:",
"LabelCatch1:",
" return",
"LabelCatch2:",
" return",
".catch java/lang/Exception from LabelTryStart to LabelTryEnd1 using LabelCatch1",
".catch java/lang/Exception from LabelTryStart to LabelTryEnd2 using LabelCatch2");
clazz.addMainMethod(
".limit stack 1",
".limit locals 1",
" ldc 2",
" invokestatic Test/foo(I)V",
" return");
runTest(builder, clazz.name, "42");
}
@Test
public void regressJsrHitParentCatchHandler() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addStaticMethod("test", ImmutableList.of(), "I",
".limit stack 2",
".limit locals 2",
" ldc 10",
" istore 1",
"TryStart:",
" iinc 1 -1",
" iload 1",
" ifeq LabelReturn",
" jsr Jsr",
" goto LabelReturn",
"Jsr:",
" astore 0",
" new java/lang/Exception",
" dup",
" invokenonvirtual java/lang/Exception.<init>()V",
" athrow",
" ret 0",
"LabelReturn:",
" ldc 0",
" ireturn",
"Catch:",
" pop",
" goto TryStart",
"TryEnd:",
".catch java/lang/Exception from TryStart to TryEnd using Catch"
);
clazz.addMainMethod(
".limit stack 2",
".limit locals 1",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" invokestatic Test/test()I",
" invokevirtual java/io/PrintStream/print(I)V",
" return");
runTest(builder, clazz.name, "0");
}
private void generateRegressJsrHitParentCatchHandler2(
JasminBuilder.ClassBuilder clazz, String name, String exception, boolean decrementInOuter) {
String outer = decrementInOuter ? " iinc 0 -1" : "";
String inner = decrementInOuter ? "" : " iinc 0 -1";
clazz.addStaticMethod(name, ImmutableList.of(), "I",
".limit stack 3",
".limit locals 1",
" ldc 10",
" istore 0",
"TryStart:",
outer,
" iload 0",
" ifeq LabelReturn1",
" jsr Jsr1",
"Jsr1:",
" pop", // Return value is not used.
"Jsr1Retry:",
inner,
" iload 0",
" ifeq LabelReturn0",
" jsr Jsr2",
" ldc 2",
" ireturn",
"Jsr2:",
" pop", // Return value is not used.
" new " + exception,
" dup",
" invokenonvirtual " + exception + ".<init>()V",
" athrow",
"Jsr2Catch:",
" pop",
" goto Jsr1Retry",
"Jsr2End:",
"LabelReturn0:",
" ldc 0",
" ireturn",
"LabelReturn1:",
" ldc 1",
" ireturn",
"Catch:",
" pop",
" goto TryStart",
"TryEnd:",
".catch java/lang/Exception from Jsr2 to Jsr2End using Jsr2Catch",
".catch java/lang/Throwable from TryStart to TryEnd using Catch"
);
}
@Test
public void regressJsrHitParentCatchHandler2() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
generateRegressJsrHitParentCatchHandler2(clazz, "test1", "java/lang/Exception", false);
generateRegressJsrHitParentCatchHandler2(clazz, "test2", "java/lang/Throwable", true);
clazz.addMainMethod(
".limit stack 2",
".limit locals 1",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" invokestatic Test/test1()I",
" invokevirtual java/io/PrintStream/print(I)V",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" invokestatic Test/test2()I",
" invokevirtual java/io/PrintStream/print(I)V",
" return");
runTest(builder, clazz.name, "01");
}
@Test
// Reduction of b/38156139 causing the infinite loop.
//
// The original code is this method:
// https://github.com/cbeust/testng/blob/4a8459e36f2b0ed057ffa7e470f1057e8e5b0ff9/src/main/java/org/testng/internal/Invoker.java#L1066
// compiled with some ancient version of javac generating code with jsr for try/finally.
public void regress38156139() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addStaticMethod("foo", ImmutableList.of("Z"), "I",
".limit stack 2",
".limit locals 3",
" ldc 10",
" istore 2",
"LabelLoopStart:",
" iinc 2 -1",
" iload 2",
" ifeq LabelReturn",
"LabelTryStart:",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" iload 2",
" invokevirtual java/io/PrintStream/print(I)V",
" jsr LabelFinally",
" goto LabelLoopStart",
"LabelTryEnd:",
"LabelReturn:",
" ldc 0",
" ireturn",
"LabelCatch:",
" pop",
" jsr LabelFinally",
" goto LabelLoopStart",
"LabelFinally:",
" astore 1",
" iload 0",
" ifeq LabelZero",
" goto LabelLoopStart", // Jump to loop start without invoking ret.
"LabelZero:",
" ret 1", // Invoke ret, which will also continue at the loop start.
".catch java/lang/Exception from LabelTryStart to LabelTryEnd using LabelCatch");
clazz.addMainMethod(
".limit stack 2",
".limit locals 1",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc 0",
" invokestatic Test/foo(Z)I",
" invokevirtual java/io/PrintStream/print(I)V",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc 1",
" invokestatic Test/foo(Z)I",
" invokevirtual java/io/PrintStream/print(I)V",
" return");
runTest(builder, clazz.name, "98765432109876543210");
}
@Test
public void regress37767254() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
// This is the code for the method org.apache.log4j.net.SocketAppender$Connector.run() from
// log4j version 1.2.
//
// https://github.com/apache/log4j/blob/v1_2-branch/src/main/java/org/apache/log4j/net/SocketAppender.java#L373
//
clazz.addVirtualMethod("run", ImmutableList.of(), "V",
".limit stack 4",
".limit locals 4",
".var 0 is this Lorg/apache/log4j/net/SocketAppender$Connector; from L0 to L26",
".var 1 is socket Ljava/net/Socket; from L5 to L13",
".var 2 is e Ljava/io/IOException; from L22 to L1",
"L0:",
".line 367",
" goto L1",
"L2:",
".line 368",
".line 369",
" aload 0",
" getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
" getfield org/apache/log4j/net/SocketAppender.reconnectionDelay I",
" i2l",
" invokestatic java/lang/Thread.sleep(J)V",
"L3:",
".line 370",
" new java/lang/StringBuffer",
" dup",
" ldc \"Attempting connection to \"",
" invokenonvirtual java/lang/StringBuffer.<init>(Ljava/lang/String;)V",
" aload 0",
" getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
" getfield org/apache/log4j/net/SocketAppender.address Ljava/net/InetAddress;",
" invokevirtual java/net/InetAddress.getHostName()Ljava/lang/String;",
" invokevirtual java/lang/StringBuffer.append(Ljava/lang/String;)Ljava/lang/StringBuffer;",
" invokevirtual java/lang/StringBuffer.toString()Ljava/lang/String;",
" invokestatic org/apache/log4j/helpers/LogLog.debug(Ljava/lang/String;)V",
"L4:",
".line 371",
" new java/net/Socket",
" dup",
" aload 0",
" getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
" getfield org/apache/log4j/net/SocketAppender.address Ljava/net/InetAddress;",
" aload 0",
" getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
" getfield org/apache/log4j/net/SocketAppender.port I",
" invokenonvirtual java/net/Socket.<init>(Ljava/net/InetAddress;I)V",
" astore 1",
"L5:",
".line 372",
" aload 0",
" astore 2",
" aload 2",
" monitorenter",
"L6:",
".line 373",
" aload 0",
" getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
" new java/io/ObjectOutputStream",
" dup",
" aload 1",
" invokevirtual java/net/Socket.getOutputStream()Ljava/io/OutputStream;",
" invokenonvirtual java/io/ObjectOutputStream.<init>(Ljava/io/OutputStream;)V",
" putfield org/apache/log4j/net/SocketAppender.oos Ljava/io/ObjectOutputStream;",
"L7:",
".line 374",
" aload 0",
" getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
" aconst_null",
" invokestatic org/apache/log4j/net/SocketAppender.access$1(Lorg/apache/log4j/net/SocketAppender;Lorg/apache/log4j/net/SocketAppender$Connector;)V",
"L8:",
".line 375",
" ldc \"Connection established. Exiting connector thread.\"",
" invokestatic org/apache/log4j/helpers/LogLog.debug(Ljava/lang/String;)V",
"L9:",
".line 376",
" jsr L10",
" goto L11",
"L12:",
".line 372",
" aload 2",
" monitorexit",
" athrow",
"L10:",
" astore 3",
" aload 2",
" monitorexit",
" ret 3",
"L13:",
".line 378",
" pop",
"L14:",
".line 379",
" ldc \"Connector interrupted. Leaving loop.\"",
" invokestatic org/apache/log4j/helpers/LogLog.debug(Ljava/lang/String;)V",
"L15:",
".line 380",
" return",
"L16:",
".line 381",
" pop",
"L17:",
".line 382",
" new java/lang/StringBuffer",
" dup",
" ldc \"Remote host \"",
" invokenonvirtual java/lang/StringBuffer.<init>(Ljava/lang/String;)V",
" aload 0",
" getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
" getfield org/apache/log4j/net/SocketAppender.address Ljava/net/InetAddress;",
" invokevirtual java/net/InetAddress.getHostName()Ljava/lang/String;",
" invokevirtual java/lang/StringBuffer.append(Ljava/lang/String;)Ljava/lang/StringBuffer;",
"L18:",
".line 383",
" ldc \" refused connection.\"",
" invokevirtual java/lang/StringBuffer.append(Ljava/lang/String;)Ljava/lang/StringBuffer;",
" invokevirtual java/lang/StringBuffer.toString()Ljava/lang/String;",
"L19:",
".line 382",
" invokestatic org/apache/log4j/helpers/LogLog.debug(Ljava/lang/String;)V",
"L20:",
".line 368",
" goto L1",
"L21:",
".line 384",
" astore 2",
"L22:",
".line 385",
" new java/lang/StringBuffer",
" dup",
" ldc \"Could not connect to \"",
" invokenonvirtual java/lang/StringBuffer.<init>(Ljava/lang/String;)V",
" aload 0",
" getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
" getfield org/apache/log4j/net/SocketAppender.address Ljava/net/InetAddress;",
" invokevirtual java/net/InetAddress.getHostName()Ljava/lang/String;",
" invokevirtual java/lang/StringBuffer.append(Ljava/lang/String;)Ljava/lang/StringBuffer;",
"L23:",
".line 386",
" ldc \". Exception is \"",
" invokevirtual java/lang/StringBuffer.append(Ljava/lang/String;)Ljava/lang/StringBuffer;",
" aload 2",
" invokevirtual java/lang/StringBuffer.append(Ljava/lang/Object;)Ljava/lang/StringBuffer;",
" invokevirtual java/lang/StringBuffer.toString()Ljava/lang/String;",
"L24:",
".line 385",
" invokestatic org/apache/log4j/helpers/LogLog.debug(Ljava/lang/String;)V",
"L25:",
".line 368",
" goto L1",
"L1:",
".line 367",
" aload 0",
" getfield org/apache/log4j/net/SocketAppender$Connector.interrupted Z",
" ifeq L2",
"L11:",
".line 365",
" return",
"L26:",
".catch all from L6 to L12 using L12",
".catch java/lang/InterruptedException from L2 to L13 using L13",
".catch java/net/ConnectException from L2 to L13 using L16",
".catch java/io/IOException from L2 to L13 using L21"
);
// Check that the code compiles without an infinite loop. It cannot run by itself.
AndroidApp app = compileWithD8(builder);
assertNotNull(app);
}
@Test
public void regress37888855Reduced() throws Exception {
JasminBuilder builder = new JasminBuilder();
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
// This is the code for the method
//
// void org.eclipse.jdt.internal.core.JavaModelOperation.run(org.eclipse.core.runtime.IProgressMonitor)
//
// from struts2/lib/core-3.1.1.jar
//
clazz.addVirtualMethod("run", ImmutableList.of("Lorg/eclipse/core/runtime/IProgressMonitor;"),
"V",
".limit stack 3",
".limit locals 15",
"TryStart1:",
"TryStart2:",
" goto Finally2Start",
"TryEnd2:",
"Catch2:",
" astore 6",
" jsr Finally2Code", // 6
" aload 6",
" athrow",
"Finally2Code:", // 9
" astore 5",
" ret 5",
"Finally2Start:",
" jsr Finally2Code", // 13
"Finally2End:",
" goto Finally1Start",
"TryEnd1:",
"Catch1:",
" astore 8",
" jsr Finally1Code", // 19
"L19:",
" aload 8",
" athrow",
"Finally1Code:",
" astore 7",
"Try3Start:",
" goto Finally3Start",
"Try3End:",
"Catch3:",
" astore 14", // Catch
" jsr Finally3Code", // 30
" aload 14",
" athrow",
"Finally3Code:", // Finally
" astore 13",
" ret 13",
"Finally3Start:",
" jsr Finally3Code", // 37
"Finally3End:",
" ret 7",
"Finally1Start:",
" jsr Finally1Code", // 41
"Finally1End:",
" return",
".catch all from TryStart2 to TryEnd2 using Catch2", // Block
".catch all from Finally2Start to Finally2End using Catch2", // Finally
".catch all from TryStart1 to TryEnd1 using Catch1", // Block
".catch all from Finally1Start to Finally1End using Catch1", // Finally
".catch all from Try3Start to Try3End using Catch3", // Block
".catch all from Finally3Start to Finally3End using Catch3" // Finally
);
// Check that the code compiles without an infinite loop. It cannot run by itself.
AndroidApp app = compileWithD8(builder);
assertNotNull(app);
}
@Test
public void regress37888855() throws Exception {
JasminBuilder builder = new JasminBuilder();
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
// This is the code for the method
//
// void org.eclipse.jdt.internal.core.JavaModelOperation.run(org.eclipse.core.runtime.IProgressMonitor)
//
// from struts2/lib/core-3.1.1.jar
//
clazz.addVirtualMethod("run", ImmutableList.of("Lorg/eclipse/core/runtime/IProgressMonitor;"), "V",
".limit stack 3",
".limit locals 15",
".var 0 is this Lorg/eclipse/jdt/internal/core/JavaModelOperation; from L0 to L50",
".var 1 is monitor Lorg/eclipse/core/runtime/IProgressMonitor; from L0 to L50",
".var 2 is manager Lorg/eclipse/jdt/internal/core/JavaModelManager; from L1 to L50",
".var 3 is deltaProcessor Lorg/eclipse/jdt/internal/core/DeltaProcessor; from L2 to L50",
".var 4 is previousDeltaCount I from L1 to L50",
".var 9 is i I from L22 to L27",
".var 10 is size I from L23 to L27",
".var 9 is i I from L28 to L38",
".var 10 is length I from L29 to L38",
".var 11 is element Lorg/eclipse/jdt/core/IJavaElement; from L32 to L37",
".var 12 is openable Lorg/eclipse/jdt/internal/core/Openable; from L33 to L37",
"L0:",
".line 705",
" invokestatic org/eclipse/jdt/internal/core/JavaModelManager.getJavaModelManager()Lorg/eclipse/jdt/internal/core/JavaModelManager;",
" astore 2",
"L1:",
".line 706",
" aload 2",
" invokevirtual org/eclipse/jdt/internal/core/JavaModelManager.getDeltaProcessor()Lorg/eclipse/jdt/internal/core/DeltaProcessor;",
" astore 3",
"L2:",
".line 707",
" aload 3",
" getfield org/eclipse/jdt/internal/core/DeltaProcessor.javaModelDeltas Ljava/util/ArrayList;",
" invokevirtual java/util/ArrayList.size()I",
" istore 4",
"L3:",
".line 709",
" aload 0",
" aload 1",
" putfield org/eclipse/jdt/internal/core/JavaModelOperation.progressMonitor Lorg/eclipse/core/runtime/IProgressMonitor;",
"L4:",
".line 710",
" aload 0",
" aload 0",
" invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.pushOperation(Lorg/eclipse/jdt/internal/core/JavaModelOperation;)V",
"L5:",
".line 712",
" aload 0",
" invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.canModifyRoots()Z",
" ifeq L6",
"L7:",
".line 715",
" invokestatic org/eclipse/jdt/internal/core/JavaModelManager.getJavaModelManager()Lorg/eclipse/jdt/internal/core/JavaModelManager;",
" getfield org/eclipse/jdt/internal/core/JavaModelManager.deltaState Lorg/eclipse/jdt/internal/core/DeltaProcessingState;",
" invokevirtual org/eclipse/jdt/internal/core/DeltaProcessingState.initializeRoots()V",
"L6:",
".line 718",
" aload 0",
" invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.executeOperation()V",
" goto L8",
"L9:",
".line 719",
" astore 6",
" jsr L10",
"L11:",
".line 723",
" aload 6",
" athrow",
"L10:",
".line 719",
" astore 5",
"L12:",
".line 720",
" aload 0",
" invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.isTopLevelOperation()Z",
" ifeq L13",
"L14:",
".line 721",
" aload 0",
" invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.runPostActions()V",
"L13:",
".line 723",
" ret 5",
"L8:",
" jsr L10",
"L15:",
" goto L16",
"L17:",
".line 724",
" astore 8",
" jsr L18",
"L19:",
".line 764",
" aload 8",
" athrow",
"L18:",
".line 724",
" astore 7",
"L20:",
".line 727",
" aload 2",
" invokevirtual org/eclipse/jdt/internal/core/JavaModelManager.getDeltaProcessor()Lorg/eclipse/jdt/internal/core/DeltaProcessor;",
" astore 3",
"L21:",
".line 730",
" iload 4",
" istore 9",
"L22:",
" aload 3",
" getfield org/eclipse/jdt/internal/core/DeltaProcessor.javaModelDeltas Ljava/util/ArrayList;",
" invokevirtual java/util/ArrayList.size()I",
" istore 10",
"L23:",
" goto L24",
"L25:",
".line 731",
" aload 3",
" aload 3",
" getfield org/eclipse/jdt/internal/core/DeltaProcessor.javaModelDeltas Ljava/util/ArrayList;",
" iload 9",
" invokevirtual java/util/ArrayList.get(I)Ljava/lang/Object;",
" checkcast org/eclipse/jdt/core/IJavaElementDelta",
" invokevirtual org/eclipse/jdt/internal/core/DeltaProcessor.updateJavaModel(Lorg/eclipse/jdt/core/IJavaElementDelta;)V",
"L26:",
".line 730",
" iinc 9 1",
"L24:",
" iload 9",
" iload 10",
" if_icmplt L25",
"L27:",
".line 737",
" iconst_0",
" istore 9",
"L28:",
" aload 0",
" getfield org/eclipse/jdt/internal/core/JavaModelOperation.resultElements [Lorg/eclipse/jdt/core/IJavaElement;",
" arraylength",
" istore 10",
"L29:",
" goto L30",
"L31:",
".line 738",
" aload 0",
" getfield org/eclipse/jdt/internal/core/JavaModelOperation.resultElements [Lorg/eclipse/jdt/core/IJavaElement;",
" iload 9",
" aaload",
" astore 11",
"L32:",
".line 739",
" aload 11",
" invokeinterface org/eclipse/jdt/core/IJavaElement.getOpenable()Lorg/eclipse/jdt/core/IOpenable; 0",
" checkcast org/eclipse/jdt/internal/core/Openable",
" astore 12",
"L33:",
".line 740",
" aload 12",
" instanceof org/eclipse/jdt/internal/core/CompilationUnit",
" ifeq L34",
" aload 12",
" checkcast org/eclipse/jdt/internal/core/CompilationUnit",
" invokevirtual org/eclipse/jdt/internal/core/CompilationUnit.isWorkingCopy()Z",
" ifne L35",
"L34:",
".line 741",
" aload 12",
" invokevirtual org/eclipse/jdt/internal/core/Openable.getParent()Lorg/eclipse/jdt/core/IJavaElement;",
" checkcast org/eclipse/jdt/internal/core/JavaElement",
" invokevirtual org/eclipse/jdt/internal/core/JavaElement.close()V",
"L35:",
".line 743",
" aload 11",
" invokeinterface org/eclipse/jdt/core/IJavaElement.getElementType()I 0",
" tableswitch 3",
" L36",
" L36",
" default: L37",
"L36:",
".line 746",
" aload 11",
" invokeinterface org/eclipse/jdt/core/IJavaElement.getJavaProject()Lorg/eclipse/jdt/core/IJavaProject; 0",
" checkcast org/eclipse/jdt/internal/core/JavaProject",
" invokevirtual org/eclipse/jdt/internal/core/JavaProject.resetCaches()V",
"L37:",
".line 737",
" iinc 9 1",
"L30:",
" iload 9",
" iload 10",
" if_icmplt L31",
"L38:",
".line 755",
" aload 0",
" invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.isTopLevelOperation()Z",
" ifeq L39",
"L40:",
".line 756",
" aload 3",
" getfield org/eclipse/jdt/internal/core/DeltaProcessor.javaModelDeltas Ljava/util/ArrayList;",
" invokevirtual java/util/ArrayList.size()I",
" iload 4",
" if_icmpgt L41",
" aload 3",
" getfield org/eclipse/jdt/internal/core/DeltaProcessor.reconcileDeltas Ljava/util/HashMap;",
" invokevirtual java/util/HashMap.isEmpty()Z",
" ifne L39",
"L41:",
".line 757",
" aload 0",
" invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.hasModifiedResource()Z",
" ifne L39",
"L42:",
".line 758",
" aload 3",
" aconst_null",
" iconst_0",
" invokevirtual org/eclipse/jdt/internal/core/DeltaProcessor.fire(Lorg/eclipse/jdt/core/IJavaElementDelta;I)V",
" goto L39",
"L43:",
".line 761",
" astore 14",
" jsr L44",
"L45:",
".line 763",
" aload 14",
" athrow",
"L44:",
".line 761",
" astore 13",
"L46:",
".line 762",
" aload 0",
" invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.popOperation()Lorg/eclipse/jdt/internal/core/JavaModelOperation;",
" pop",
"L47:",
".line 763",
" ret 13",
"L39:",
" jsr L44",
"L48:",
".line 764",
" ret 7",
"L16:",
" jsr L18",
"L49:",
".line 765",
" return",
"L50:",
".catch all from L5 to L9 using L9",
".catch all from L8 to L15 using L9",
".catch all from L3 to L17 using L17",
".catch all from L16 to L49 using L17",
".catch all from L20 to L43 using L43",
".catch all from L39 to L48 using L43"
);
// Check that the code compiles without an infinite loop. It cannot run by itself.
AndroidApp app = compileWithD8(builder);
assertNotNull(app);
}
// Some jsr tests that fails with org.objectweb.asm.commons.JSRInlinerAdapter.
@Test
// This test is based on the example on http://asm.ow2.org/doc/developer-guide.html.
public void testJsrWithNestedJsrRetBasedOnControlFlow() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_3);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addStaticMethod("foo", ImmutableList.of("Z"), "I",
".limit stack 2",
".limit locals 3",
" jsr LabelSub1",
" ldc 1",
" ireturn",
"LabelSub1:",
" astore 1",
" jsr LabelSub2",
" ldc 0",
" ireturn",
"LabelSub2:",
" astore 2",
" iload 0",
" ifeq LabelZero",
" ret 2",
"LabelZero:",
" ret 1");
clazz.addMainMethod(
".limit stack 2",
".limit locals 1",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc 0",
" invokestatic Test/foo(Z)I",
" invokevirtual java/io/PrintStream/print(I)V",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc 1",
" invokestatic Test/foo(Z)I",
" invokevirtual java/io/PrintStream/print(I)V",
" return");
String expected = "10";
String javaResult = runOnJava(builder, clazz.name);
assertEquals(expected, javaResult);
String artResult = runOnArtD8(builder, clazz.name);
// The ASM jsr inliner does not get the control-flow dependent ret right in his case.
assertNotEquals(expected, artResult);
String cfFrontendResult = runOnJavaR8CfFrontend(builder, clazz.name);
assertNotEquals(expected, cfFrontendResult);
// This fails with dx.
expectDxFailure(builder);
}
@Test
// This test is based on the example on http://asm.ow2.org/doc/developer-guide.html.
public void testJsrWithNestedRetBasedOnControlFlow2() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addStaticMethod("foo", ImmutableList.of("Z"), "I",
".limit stack 2",
".limit locals 3",
" jsr LabelSub1",
" ldc 1",
" ireturn",
"LabelSub1:",
" jsr LabelSub2",
" ldc 0",
" ireturn",
"LabelSub2:",
" astore 2",
" astore 1",
" iload 0",
" ifeq LabelZero",
" ret 2",
"LabelZero:",
" ret 1");
clazz.addMainMethod(
".limit stack 2",
".limit locals 1",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc 0",
" invokestatic Test/foo(Z)I",
" invokevirtual java/io/PrintStream/print(I)V",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc 1",
" invokestatic Test/foo(Z)I",
" invokevirtual java/io/PrintStream/print(I)V",
" return");
String expected = "10";
String javaResult = runOnJava(builder, clazz.name);
assertEquals(expected, javaResult);
String artResult = runOnArtD8(builder, clazz.name);
// The ASM jsr inliner does not get the control-flow dependent ret right in his case.
assertNotEquals(expected, artResult);
String cfFrontendResult = runOnJavaR8CfFrontend(builder, clazz.name);
assertNotEquals(expected, cfFrontendResult);
// This fails with dx.
expectDxFailure(builder);
}
@Test
// This test is based on the example on http://asm.ow2.org/doc/developer-guide.html.
public void testJsrWithNestedRetBasedOnControlFlow3() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addStaticMethod("foo", ImmutableList.of("Z"), "I",
".limit stack 2",
".limit locals 3",
" jsr LabelSub1",
" ldc 1",
" ireturn",
"LabelSub1:",
" jsr LabelSub2",
" ldc 0",
" ireturn",
"LabelSub2:",
" swap",
" astore 1",
" astore 2",
" iload 0",
" ifeq LabelZero",
" ret 2",
"LabelZero:",
" ret 1");
clazz.addMainMethod(
".limit stack 2",
".limit locals 1",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc 0",
" invokestatic Test/foo(Z)I",
" invokevirtual java/io/PrintStream/print(I)V",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc 1",
" invokestatic Test/foo(Z)I",
" invokevirtual java/io/PrintStream/print(I)V",
" return");
String expected = "10";
String javaResult = runOnJava(builder, clazz.name);
assertEquals(expected, javaResult);
String artResult = runOnArtD8(builder, clazz.name);
// The ASM jsr inliner does not get the control-flow dependent ret right in his case.
assertNotEquals(expected, artResult);
String cfFrontendResult = runOnJavaR8CfFrontend(builder, clazz.name);
assertNotEquals(expected, cfFrontendResult);
// This fails with dx.
expectDxFailure(builder);
}
// Some jsr tests that fails bytecode verification on the Java VM.
@Test
public void testReuseAddr() throws Exception {
JasminBuilder builder = new JasminBuilder();
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addMainMethod(
".limit stack 3",
".limit locals 2",
" jsr LabelSub1",
" return",
"LabelSub1:",
" jsr LabelSub2",
" dup",
" astore 0",
" ret 0",
"LabelSub2:",
" dup",
" astore 0",
" ret 0");
// This does not run on the Java VM (verification error).
assertNotEquals(0, runOnJavaRaw(builder, clazz.name));
}
@Test
public void testSwitchAddr() throws Exception {
JasminBuilder builder = new JasminBuilder();
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addMainMethod(
".limit stack 3",
".limit locals 2",
" jsr LabelSub1",
" astore 0",
" ret 0",
"LabelSub1:",
" jsr LabelSub2",
" return",
"LabelSub2:",
" swap",
" astore 0",
" ret 0");
// This does not run on the Java VM (verification error).
assertNotEquals(0, runOnJavaRaw(builder, clazz.name));
}
@Test
public void testJsrPreserveRetAddressOverJsr() throws Exception {
JasminBuilder builder = new JasminBuilder();
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
clazz.addStaticMethod("foo", ImmutableList.of("Z"), "I",
".limit stack 2",
".limit locals 2",
" jsr LabelSub1",
" ldc 0",
" ireturn",
"LabelSub1:",
" iload 0",
" ifeq StoreInFirst",
" pop",
" goto NextJsr",
"StoreInFirst:",
" astore 1",
"NextJsr:",
" jsr LabelSub2",
" ldc 1",
" ireturn",
"LabelSub2:",
" iload 0",
" ifeq DontStoreInSecond", // Same as StoreInFirst
" astore 1",
" goto Ret",
"DontStoreInSecond:",
" pop",
"Ret:",
// There will always be an address in local 1 at this point.
" ret 1");
clazz.addMainMethod(
".limit stack 2",
".limit locals 1",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc 0",
" invokestatic Test/foo(Z)I",
" invokevirtual java/io/PrintStream/print(I)V",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc 1",
" invokestatic Test/foo(Z)I",
" invokevirtual java/io/PrintStream/print(I)V",
" return");
// This does not run on the Java VM (verification error).
assertNotEquals(0, runOnJavaRaw(builder, clazz.name));
}
}