Reland "Reland "Add regression test for missing locals in desugared lambdas.""

This reverts commit cc106188deeedf2dab3963e7e7b50e097611cc34.

Reason for revert: Pinned JDK9 should resolve the bot issue.

Bug: 123390221
Change-Id: Ia80174ab6a9638a6d4f6aa2e705bbee306480b77
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index fb61f45..1c0287e 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.naming.MemberNaming;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.TestDescriptionWatcher;
@@ -50,6 +51,7 @@
 import org.apache.harmony.jpda.tests.framework.jdwp.EventPacket;
 import org.apache.harmony.jpda.tests.framework.jdwp.Frame.Variable;
 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands.ObjectReferenceCommandSet;
 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands.ReferenceTypeCommandSet;
 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands.StackFrameCommandSet;
 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
@@ -153,6 +155,12 @@
   }
 
   protected final void runDebugTest(
+      DebugTestConfig config, Class<?> debuggeeClass, JUnit3Wrapper.Command... commands)
+      throws Throwable {
+    runInternal(config, debuggeeClass.getTypeName(), Arrays.asList(commands));
+  }
+
+  protected final void runDebugTest(
       DebugTestConfig config, String debuggeeClass, JUnit3Wrapper.Command... commands)
       throws Throwable {
     runInternal(config, debuggeeClass, Arrays.asList(commands));
@@ -324,6 +332,10 @@
     return new JUnit3Wrapper.Command.RunCommand();
   }
 
+  protected final JUnit3Wrapper.Command breakpoint(MethodReference method) {
+    return breakpoint(method.getHolderClass().getTypeName(), method.getMethodName());
+  }
+
   protected final JUnit3Wrapper.Command breakpoint(String className, String methodName) {
     return breakpoint(className, methodName, null);
   }
@@ -477,6 +489,10 @@
     });
   }
 
+  protected final JUnit3Wrapper.Command checkLine(int line) {
+    return inspect(t -> t.checkLine(null, line));
+  }
+
   protected final JUnit3Wrapper.Command checkLine(String sourceFile, int line) {
     return inspect(t -> t.checkLine(sourceFile, line));
   }
@@ -532,6 +548,16 @@
     });
   }
 
+  protected final JUnit3Wrapper.Command checkFieldOnThis(
+      String fieldName, String fieldSignature, Value expectedValue) {
+    return inspect(
+        t -> {
+          Value value = t.getFieldOnThis(fieldName, fieldSignature);
+          Assert.assertEquals(
+              "Incorrect value for field 'this." + fieldName + "'", expectedValue, value);
+        });
+  }
+
   protected final JUnit3Wrapper.Command inspect(Consumer<JUnit3Wrapper.DebuggeeState> inspector) {
     return t -> inspector.accept(t.debuggeeState);
   }
@@ -1298,6 +1324,7 @@
 
         @Override
         public void checkLine(String sourceFile, int line) {
+          sourceFile = sourceFile != null ? sourceFile : getSourceFile();
           if (!Objects.equals(sourceFile, getSourceFile()) || line != getLineNumber()) {
             String locationString = convertCurrentLocationToString();
             Assert.fail(
@@ -1501,7 +1528,15 @@
 
         // The class is available, lookup and read the field.
         long fieldId = findField(getMirror(), classId, fieldName, fieldSignature);
-        return getField(getMirror(), classId, fieldId);
+        return internalStaticField(getMirror(), classId, fieldId);
+      }
+
+      public Value getFieldOnThis(String fieldName, String fieldSignature) {
+        long thisObjectId = getMirror().getThisObject(getThreadId(), getFrameId());
+        long classId = getMirror().getReferenceType(thisObjectId);
+        // TODO(zerny): Search supers too. This will only get the field if directly on the class.
+        long fieldId = findField(getMirror(), classId, fieldName, fieldSignature);
+        return internalInstanceField(getMirror(), thisObjectId, fieldId);
       }
 
       private long findField(VmMirror mirror, long classId, String fieldName,
@@ -1547,10 +1582,10 @@
         return matchingFieldIds.getLong(0);
       }
 
-      private Value getField(VmMirror mirror, long classId, long fieldId) {
-
-        CommandPacket commandPacket = new CommandPacket(ReferenceTypeCommandSet.CommandSetID,
-            ReferenceTypeCommandSet.GetValuesCommand);
+      private static Value internalStaticField(VmMirror mirror, long classId, long fieldId) {
+        CommandPacket commandPacket =
+            new CommandPacket(
+                ReferenceTypeCommandSet.CommandSetID, ReferenceTypeCommandSet.GetValuesCommand);
         commandPacket.setNextValueAsReferenceTypeID(classId);
         commandPacket.setNextValueAsInt(1);
         commandPacket.setNextValueAsFieldID(fieldId);
@@ -1565,6 +1600,23 @@
       }
     }
 
+    private static Value internalInstanceField(VmMirror mirror, long objectId, long fieldId) {
+      CommandPacket commandPacket =
+          new CommandPacket(
+              ObjectReferenceCommandSet.CommandSetID, ObjectReferenceCommandSet.GetValuesCommand);
+      commandPacket.setNextValueAsObjectID(objectId);
+      commandPacket.setNextValueAsInt(1);
+      commandPacket.setNextValueAsFieldID(fieldId);
+      ReplyPacket replyPacket = mirror.performCommand(commandPacket);
+      assert replyPacket.getErrorCode() == Error.NONE;
+
+      int fieldsCount = replyPacket.getNextValueAsInt();
+      assert fieldsCount == 1;
+      Value result = replyPacket.getNextValueAsValue();
+      Assert.assertTrue(replyPacket.isAllDataRead());
+      return result;
+    }
+
     public static Optional<Variable> getVariableAt(VmMirror mirror, Location location,
         String localName) {
       return getVariablesAt(mirror, location).stream()
diff --git a/src/test/java/com/android/tools/r8/debug/LambdaOuterContextTest.java b/src/test/java/com/android/tools/r8/debug/LambdaOuterContextTest.java
new file mode 100644
index 0000000..9991b57
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/LambdaOuterContextTest.java
@@ -0,0 +1,32 @@
+// Copyright (c) 2019, 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.debug;
+
+public class LambdaOuterContextTest {
+
+  public interface Converter {
+    String convert(int value);
+  }
+
+  public int outer;
+
+  public LambdaOuterContextTest(int outer) {
+    this.outer = outer;
+  }
+
+  public void foo(Converter converter) {
+    System.out.println(converter.convert(outer));
+  }
+
+  public void bar(int arg) {
+    foo(value -> {
+      // Ensure lambda uses parts of the outer context, otherwise javac will optimize it out.
+      return Integer.toString(outer + value + arg);
+    });
+  }
+
+  public static void main(String[] args) {
+    new LambdaOuterContextTest(args.length + 42).bar(args.length);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/LambdaOuterContextTestRunner.java b/src/test/java/com/android/tools/r8/debug/LambdaOuterContextTestRunner.java
new file mode 100644
index 0000000..8af2a6e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/LambdaOuterContextTestRunner.java
@@ -0,0 +1,67 @@
+// Copyright (c) 2019, 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.debug;
+
+import static com.android.tools.r8.references.Reference.methodFromMethod;
+
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.JvmTestBuilder;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.debug.LambdaOuterContextTest.Converter;
+import com.android.tools.r8.utils.StringUtils;
+import org.apache.harmony.jpda.tests.framework.jdwp.Value;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class LambdaOuterContextTestRunner extends DebugTestBase {
+
+  public static final Class<?> CLASS = LambdaOuterContextTest.class;
+  public static final String EXPECTED = StringUtils.lines("84");
+
+  @Test
+  public void testJvm() throws Throwable {
+    JvmTestBuilder jvmTestBuilder = testForJvm().addTestClasspath();
+    jvmTestBuilder.run(CLASS).assertSuccessWithOutput(EXPECTED);
+    runDebugger(jvmTestBuilder.debugConfig());
+  }
+
+  @Test
+  @Ignore("b/123068053")
+  public void testD8() throws Throwable {
+    D8TestCompileResult compileResult =
+        testForD8().addProgramClassesAndInnerClasses(CLASS).compile();
+    compileResult.run(CLASS).assertSuccessWithOutput(EXPECTED);
+    runDebugger(compileResult.debugConfig());
+  }
+
+  @Test
+  public void testR8Cf() throws Throwable {
+    R8TestCompileResult compileResult =
+        testForR8(Backend.CF)
+            .addProgramClassesAndInnerClasses(CLASS)
+            .debug()
+            .noMinification()
+            .noTreeShaking()
+            .compile();
+    compileResult.run(CLASS).assertSuccessWithOutput(EXPECTED);
+    runDebugger(compileResult.debugConfig());
+  }
+
+  private void runDebugger(DebugTestConfig config) throws Throwable {
+    runDebugTest(
+        config,
+        CLASS,
+        breakpoint(methodFromMethod(CLASS.getMethod("foo", Converter.class))),
+        run(),
+        checkLine(19),
+        checkLocals("this", "converter"),
+        checkFieldOnThis("outer", null, Value.createInt(42)),
+        stepInto(INTELLIJ_FILTER),
+        checkLine(25),
+        checkLocals("this", "value", "arg"),
+        checkNoLocal("outer"),
+        checkFieldOnThis("outer", null, Value.createInt(42)),
+        run());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/LambdaTest.java b/src/test/java/com/android/tools/r8/debug/LambdaTest.java
index 1b413ba..f9e6c34 100644
--- a/src/test/java/com/android/tools/r8/debug/LambdaTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LambdaTest.java
@@ -13,7 +13,6 @@
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-// TODO(shertz) test local variables
 @RunWith(Parameterized.class)
 public class LambdaTest extends DebugTestBase {
 
@@ -47,8 +46,11 @@
         run(),
         checkMethod(debuggeeClass, initialMethodName),
         checkLine(SOURCE_FILE, 12),
+        checkLocals("i"),
+        checkNoLocal("j"),
         stepInto(INTELLIJ_FILTER),
         checkLine(SOURCE_FILE, 16),
+        checkLocals("i", "j"),
         run());
   }
 
@@ -63,8 +65,10 @@
         run(),
         checkMethod(debuggeeClass, initialMethodName),
         checkLine(SOURCE_FILE, 32),
+        checkLocals("i", "a", "b"),
         stepInto(INTELLIJ_FILTER),
         checkLine(SOURCE_FILE, 37),
+        checkLocals("a", "b"),
         run());
   }