blob: a6505c8f22c1a7196919b2e9e6d6b5cd1dcbe77b [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 com.android.tools.r8.ToolHelper;
import com.android.tools.r8.debuginfo.DebugInfoInspector;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexDebugInfo;
import com.android.tools.r8.jasmin.JasminBuilder.ClassFileVersion;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import org.junit.Test;
public class DebugLocalTests extends JasminTestBase {
@Test
public void testSwap() throws Exception {
JasminBuilder builder = new JasminBuilder();
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
MethodSignature foo = clazz.addVirtualMethod("foo", ImmutableList.of("Ljava/lang/String;"), "V",
// The first three vars are out-of-order to verify that the order is not relied on.
".var 5 is t I from L4 to L6",
".var 1 is bar Ljava/lang/String; from L0 to L9",
".var 0 is this LTest; from L0 to L9",
".var 2 is x I from L1 to L9",
".var 3 is y I from L2 to L9",
".var 4 is z I from L3 to L9",
".var 5 is foobar Ljava/lang/String; from L7 to L9",
".limit locals 6",
".limit stack 2",
"L0:",
".line 23",
" iconst_1",
" istore 2",
"L1:",
".line 24",
" iconst_2",
" istore 3",
"L2:",
".line 25",
" iconst_3",
" istore 4",
"L3:",
" .line 27",
" iload 3",
" istore 5",
"L4:",
" .line 28",
" iload 2",
" istore 3",
"L5:",
" .line 29",
" iload 5",
" istore 2",
"L6:",
" .line 32",
" new java/lang/StringBuilder",
" dup",
" invokespecial java/lang/StringBuilder/<init>()V",
" ldc \"And the value of y is: \"",
" invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
" iload 2",
" invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;",
" iload 3",
" invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;",
" iload 4",
" invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;",
" invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;",
" astore 5",
"L7:",
" .line 34",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" aload 5",
" invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
"L8:",
" .line 35",
" return",
"L9:");
clazz.addMainMethod(
".limit stack 3",
".limit locals 1",
" new Test",
" dup",
" invokespecial Test/<init>()V",
" ldc \"Fsg\"",
" invokevirtual Test/foo(Ljava/lang/String;)V",
" return");
String expected = "And the value of y is: 213";
String javaResult = runOnJava(builder, clazz.name);
assertEquals(expected, javaResult);
AndroidApp jasminApp = builder.build();
AndroidApp d8App = ToolHelper.runD8(jasminApp);
CodeInspector inspector = new CodeInspector(d8App);
ClassSubject classSubject = inspector.clazz("Test");
MethodSubject methodSubject = classSubject.method(foo);
DexCode code = methodSubject.getMethod().getCode().asDexCode();
DexDebugInfo info = code.getDebugInfo();
assertEquals(23, info.startLine);
assertEquals(1, info.parameters.length);
assertEquals("bar", info.parameters[0].toString());
// TODO(zerny): Verify the debug computed locals information.
String artResult = runOnArt(d8App, clazz.name);
assertEquals(expected, artResult);
}
@Test
public void testNoLocalInfoOnStack() throws Exception {
JasminBuilder builder = new JasminBuilder();
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
MethodSignature foo = clazz.addVirtualMethod("foo", ImmutableList.of(), "I",
".var 0 is this LTest; from Init to End",
".var 1 is x I from XStart to XEnd",
".limit locals 2",
".limit stack 1",
"Init:",
".line 1",
" ldc 0",
" istore 1",
"XStart:",
".line 2",
" ldc 42",
" istore 1",
" iload 1",
"XEnd:",
".line 3",
" ireturn",
"End:");
clazz.addMainMethod(
".limit stack 3",
".limit locals 1",
" new Test",
" dup",
" invokespecial Test/<init>()V",
" invokevirtual Test/foo()I",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" swap",
" invokevirtual java/io/PrintStream/print(I)V",
" return");
String expected = "42";
String javaResult = runOnJava(builder, clazz.name);
assertEquals(expected, javaResult);
AndroidApp jasminApp = builder.build();
AndroidApp d8App = ToolHelper.runD8(jasminApp);
DebugInfoInspector info = new DebugInfoInspector(d8App, clazz.name, foo);
info.checkStartLine(1);
info.checkLineHasExactLocals(1, "this", "Test");
info.checkLineHasExactLocals(2, "this", "Test", "x", "int");
info.checkLineHasExactLocals(3, "this", "Test");
String artResult = runOnArt(d8App, clazz.name);
assertEquals(expected, artResult);
}
// Check that we properly handle switching a local slot from one variable to another.
@Test
public void checkLocalChange() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
MethodSignature foo = clazz.addStaticMethod("foo", ImmutableList.of("I"), "I",
".limit stack 2",
".limit locals 2",
".var 0 is param I from MethodStart to MethodEnd",
".var 1 is x I from LabelXStart to LabelXEnd",
".var 1 is y I from LabelYStart to LabelYEnd",
"MethodStart:",
".line 1",
" ldc 0",
" istore 1",
"LabelXStart:",
".line 2",
" invokestatic Test/ensureLine()V",
"LabelXEnd:",
" iload 0",
" lookupswitch",
" 1: Case1",
" default: CaseDefault",
"Case1:",
".line 3",
" ldc 42",
" istore 1",
"LabelYStart:",
".line 4",
" invokestatic Test/ensureLine()V",
" goto AfterSwitch",
"CaseDefault:",
".line 5",
" ldc -42",
" istore 1",
".line 6",
" invokestatic Test/ensureLine()V",
"AfterSwitch:",
".line 7",
" iload 1",
" ireturn",
"LabelYEnd:",
"MethodEnd:"
);
clazz.addStaticMethod("ensureLine", ImmutableList.of(), "V",
".limit stack 0",
".limit locals 0",
" return");
clazz.addMainMethod(
".limit stack 2",
".limit locals 1",
" ldc 0",
" invokestatic Test/foo(I)I",
" ldc 1",
" invokestatic Test/foo(I)I",
" pop",
" return");
String expected = "";
String javaResult = runOnJava(builder, clazz.name);
assertEquals(expected, javaResult);
AndroidApp jasminApp = builder.build();
AndroidApp d8App = ToolHelper.runD8(jasminApp);
String artResult = runOnArt(d8App, clazz.name);
assertEquals(expected, artResult);
DebugInfoInspector info = new DebugInfoInspector(d8App, clazz.name, foo);
info.checkStartLine(1);
info.checkLineHasExactLocals(1, "param", "int");
info.checkLineHasExactLocals(2, "param", "int", "x", "int");
info.checkLineHasExactLocals(4, "param", "int", "y", "int");
info.checkLineHasExactLocals(6, "param", "int", "y", "int");
info.checkLineHasExactLocals(7, "param", "int", "y", "int");
}
@Test
public void testLocalManyRanges() throws Exception {
JasminBuilder builder = new JasminBuilder();
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
MethodSignature foo = clazz.addStaticMethod("foo", ImmutableList.of("I"), "I",
".limit stack 2",
".limit locals 2",
".var 0 is param I from Init to End",
".var 1 is x I from LabelStart1 to LabelEnd1",
".var 1 is x I from LabelStart2 to LabelEnd2",
".var 1 is x I from LabelStart3 to LabelEnd3",
".var 1 is x I from LabelStart4 to LabelEnd4",
"Init:",
".line 1",
" iload 0",
" istore 1",
"LabelStart1:",
".line 2",
" invokestatic Test/ensureLine()V",
"LabelEnd1:",
".line 3",
" invokestatic Test/ensureLine()V",
"LabelStart2:",
".line 4",
" invokestatic Test/ensureLine()V",
"LabelEnd2:",
".line 5",
" invokestatic Test/ensureLine()V",
"LabelStart3:",
".line 6",
" invokestatic Test/ensureLine()V",
"LabelEnd3:",
".line 7",
" invokestatic Test/ensureLine()V",
"LabelStart4:",
".line 8",
" invokestatic Test/ensureLine()V",
"LabelEnd4:",
".line 9",
" invokestatic Test/ensureLine()V",
" iload 1",
" ireturn",
"End:"
);
clazz.addStaticMethod("ensureLine", ImmutableList.of(), "V",
".limit stack 0",
".limit locals 0",
" return");
clazz.addMainMethod(
".limit stack 2",
".limit locals 1",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc 42",
" invokestatic Test/foo(I)I",
" invokevirtual java/io/PrintStream/print(I)V",
" return");
String expected = "42";
String javaResult = runOnJava(builder, clazz.name);
assertEquals(expected, javaResult);
AndroidApp jasminApp = builder.build();
AndroidApp d8App = ToolHelper.runD8(jasminApp);
String artResult = runOnArt(d8App, clazz.name);
assertEquals(expected, artResult);
DebugInfoInspector info = new DebugInfoInspector(d8App, clazz.name, foo);
info.checkStartLine(1);
info.checkLineHasExactLocals(1, "param", "int");
info.checkLineHasExactLocals(2, "param", "int", "x", "int");
info.checkLineHasExactLocals(3, "param", "int");
info.checkLineHasExactLocals(4, "param", "int", "x", "int");
info.checkLineHasExactLocals(5, "param", "int");
info.checkLineHasExactLocals(6, "param", "int", "x", "int");
info.checkLineHasExactLocals(7, "param", "int");
info.checkLineHasExactLocals(8, "param", "int", "x", "int");
info.checkLineHasExactLocals(9, "param", "int");
}
@Test
public void argumentLiveAtReturn() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
/*
This is the original Java source code.
public static int argumentLiveAtReturn(int x) { // Line 1
switch (x) {
case 0:
return 0;
case 1:
return 0;
case 2:
return 0;
case 100:
return 1;
case 101:
return 1;
case 102:
return 1;
}
return -1;
}
*/
MethodSignature foo = clazz.addStaticMethod("argumentLiveAtReturn", ImmutableList.of("I"), "I",
".limit stack 2",
".limit locals 1",
".var 0 is x I from L0 to L8",
"L0:",
".line 2",
" iload 0",
"lookupswitch",
" 0: L1",
" 1: L2",
" 2: L3",
" 100: L4",
" 101: L5",
" 102: L6",
" default: L7",
"L1:",
".line 4",
" iconst_0",
" ireturn",
"L2:",
".line 6",
" iconst_0",
" ireturn",
"L3:",
".line 8",
" iconst_0",
" ireturn",
"L4:",
".line 10",
" iconst_1",
" ireturn",
"L5:",
".line 12",
" iconst_1",
" ireturn",
"L6:",
".line 14",
" iconst_1",
" ireturn",
"L7:",
".line 16",
" iconst_m1",
" ireturn",
"L8:"
);
clazz.addMainMethod(
".limit stack 2",
".limit locals 1",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc -1",
" invokestatic Test/argumentLiveAtReturn(I)I",
" invokevirtual java/io/PrintStream/print(I)V",
" return");
String expected = "-1";
String javaResult = runOnJava(builder, clazz.name);
assertEquals(expected, javaResult);
AndroidApp jasminApp = builder.build();
AndroidApp d8App = ToolHelper.runD8(jasminApp);
String artResult = runOnArt(d8App, clazz.name);
assertEquals(expected, artResult);
DebugInfoInspector info = new DebugInfoInspector(d8App, clazz.name, foo);
info.checkStartLine(2);
info.checkLineHasExactLocals(2, "x", "int");
info.checkLineHasExactLocals(4, "x", "int");
info.checkLineHasExactLocals(6, "x", "int");
info.checkLineHasExactLocals(8, "x", "int");
info.checkLineHasExactLocals(10, "x", "int");
info.checkLineHasExactLocals(12, "x", "int");
info.checkLineHasExactLocals(14, "x", "int");
info.checkLineHasExactLocals(16, "x", "int");
}
@Test
public void testLocalSwitchRewriteToIfs() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
/*
This is the original Java source code. The code generated by javac have been
slightly modified below to end local t on the lookupswitch instruction.
public static int switchRewrite(int x) { // Line 1
{
int t = x + 1;
x = t;
x = x + x;
}
switch (x) {
case 1:
return 7;
case 100:
return 8;
}
return -1;
}
*/
MethodSignature foo = clazz.addStaticMethod("switchRewrite", ImmutableList.of("I"), "I",
".limit stack 2",
".limit locals 2",
".var 0 is x I from L0 to L7",
".var 1 is t I from L1 to L3",
"L0:",
".line 3",
" iload 0",
" iconst_1",
" iadd",
" istore 1",
"L1:",
".line 4",
" iload 1",
" istore 0",
"L2:",
".line 5",
" iload 0",
" iload 0",
" iadd",
" istore 0",
"L3_ORIGINAL:", // This is where javac normally ends t.
".line 7",
" iload 0",
"lookupswitch",
" 0: L4",
" 100: L5",
" default: L6",
"L3:", // Moved L3 here to end t on the switch instruction.
"L4:",
".line 9",
" iconst_0",
" ireturn",
"L5:",
".line 11",
" iconst_1",
" ireturn",
"L6:",
".line 13",
" iconst_m1",
" ireturn",
"L7:"
);
clazz.addMainMethod(
".limit stack 2",
".limit locals 1",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc 1",
" invokestatic Test/switchRewrite(I)I",
" invokevirtual java/io/PrintStream/print(I)V",
" return");
String expected = "-1";
String javaResult = runOnJava(builder, clazz.name);
assertEquals(expected, javaResult);
AndroidApp jasminApp = builder.build();
AndroidApp d8App = ToolHelper.runD8(jasminApp);
String artResult = runOnArt(d8App, clazz.name);
assertEquals(expected, artResult);
DebugInfoInspector info = new DebugInfoInspector(d8App, clazz.name, foo);
info.checkStartLine(3);
info.checkLineHasExactLocals(3, "x", "int");
info.checkLineHasExactLocals(4, "x", "int", "t", "int");
info.checkLineHasExactLocals(5, "x", "int", "t", "int");
info.checkLineHasExactLocals(7, "x", "int", "t", "int");
info.checkLineHasExactLocals(9, "x", "int");
info.checkLineHasExactLocals(11, "x", "int");
info.checkLineHasExactLocals(13, "x", "int");
}
@Test
public void testLocalSwitchRewriteToSwitches() throws Exception {
JasminBuilder builder = new JasminBuilder(ClassFileVersion.JDK_1_4);
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
/*
This is the original Java source code. The code generated by javac have been
slightly modified below to end local t on the lookupswitch instruction.
public static int switchRewrite(int x) { // Line 1
{
int t = x + 1;
x = t;
x = x + x;
}
switch (x) {
case 0:
return 0;
case 1:
return 0;
case 2:
return 0;
case 100:
return 1;
case 101:
return 1;
case 102:
return 1;
}
return -1;
}
*/
MethodSignature foo = clazz.addStaticMethod("switchRewrite", ImmutableList.of("I"), "I",
".limit stack 2",
".limit locals 2",
".var 0 is x I from L0 to L11",
".var 1 is t I from L1 to L3",
"L0:",
".line 3",
" iload 0",
" iconst_1",
" iadd",
" istore 1",
"L1:",
".line 4",
" iload 1",
" istore 0",
"L2:",
".line 5",
" iload 0",
" iload 0",
" iadd",
" istore 0",
"L3_ORIGINAL:", // This is where javac normally ends t.
".line 7",
" iload 0",
"lookupswitch",
" 0: L4",
" 1: L5",
" 2: L6",
" 100: L7",
" 101: L8",
" 102: L9",
" default: L10",
"L3:", // Moved L3 here to end t on the switch instruction.
"L4:",
".line 9",
" iconst_0",
" ireturn",
"L5:",
".line 11",
" iconst_0",
" ireturn",
"L6:",
".line 13",
" iconst_0",
" ireturn",
"L7:",
".line 15",
" iconst_1",
" ireturn",
"L8:",
".line 17",
" iconst_1",
" ireturn",
"L9:",
".line 19",
" iconst_1",
" ireturn",
"L10:",
".line 21",
" iconst_m1",
" ireturn",
"L11:"
);
clazz.addMainMethod(
".limit stack 2",
".limit locals 1",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc 1",
" invokestatic Test/switchRewrite(I)I",
" invokevirtual java/io/PrintStream/print(I)V",
" return");
String expected = "-1";
String javaResult = runOnJava(builder, clazz.name);
assertEquals(expected, javaResult);
AndroidApp jasminApp = builder.build();
AndroidApp d8App = ToolHelper.runD8(jasminApp);
String artResult = runOnArt(d8App, clazz.name);
assertEquals(expected, artResult);
DebugInfoInspector info = new DebugInfoInspector(d8App, clazz.name, foo);
info.checkStartLine(3);
info.checkLineHasExactLocals(3, "x", "int");
info.checkLineHasExactLocals(4, "x", "int", "t", "int");
info.checkLineHasExactLocals(5, "x", "int", "t", "int");
info.checkLineHasExactLocals(7, "x", "int", "t", "int");
info.checkLineHasExactLocals(9, "x", "int");
info.checkLineHasExactLocals(11, "x", "int");
info.checkLineHasExactLocals(13, "x", "int");
}
@Test
public void testLocalEndAfterLine() throws Exception {
JasminBuilder builder = new JasminBuilder();
JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
MethodSignature foo = clazz.addStaticMethod("foo", ImmutableList.of("I"), "I",
".limit stack 2",
".limit locals 2",
".var 0 is x I from L0 to L4",
".var 1 is t I from L1 to L3",
"L0:",
".line 1",
" iload 0",
" iconst_1",
" iadd",
" istore 1",
"L1:",
".line 2",
" iload 1",
" istore 0",
"L2:",
".line 3",
" iload 0",
" iload 0",
" iadd",
" istore 0",
".line 4",
" iload 0",
"L3:", // This should not end t on line 4!
".line 5",
" ireturn",
"L4:"
);
clazz.addMainMethod(
".limit stack 2",
".limit locals 1",
" getstatic java/lang/System/out Ljava/io/PrintStream;",
" ldc 1",
" invokestatic Test/foo(I)I",
" invokevirtual java/io/PrintStream/print(I)V",
" return");
String expected = "4";
String javaResult = runOnJava(builder, clazz.name);
assertEquals(expected, javaResult);
AndroidApp jasminApp = builder.build();
AndroidApp d8App = ToolHelper.runD8(jasminApp);
String artResult = runOnArt(d8App, clazz.name);
assertEquals(expected, artResult);
DebugInfoInspector info = new DebugInfoInspector(d8App, clazz.name, foo);
info.checkStartLine(1);
info.checkLineHasExactLocals(1, "x", "int");
info.checkLineHasExactLocals(2, "x", "int", "t", "int");
info.checkLineHasExactLocals(3, "x", "int", "t", "int");
info.checkLineHasExactLocals(4, "x", "int", "t", "int");
info.checkLineHasExactLocals(5, "x", "int");
}
}