| // 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.EventBasedDebugInfo; |
| 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(); |
| EventBasedDebugInfo info = code.getDebugInfo().asEventBasedInfo(); |
| 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"); |
| } |
| } |