Enable debugging tests on Dalvik

This CL enables tests in package com.android.tools.r8.debug for
Dalvik and updates jdwp test framework for Dalvik.

Bug: 65869324
Change-Id: I41d09c7cb619670494f43fe3e3c00e8e9ba0a58a
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 1f9101c..cc01eab 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -52,6 +52,7 @@
 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.SuspendPolicy;
 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.TypeTag;
 import org.apache.harmony.jpda.tests.framework.jdwp.Location;
+import org.apache.harmony.jpda.tests.framework.jdwp.Method;
 import org.apache.harmony.jpda.tests.framework.jdwp.ParsedEvent;
 import org.apache.harmony.jpda.tests.framework.jdwp.ParsedEvent.EventThread;
 import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
@@ -91,9 +92,7 @@
   // Set to true to enable verbose logs
   private static final boolean DEBUG_TESTS = false;
 
-  // Dalvik does not support command ReferenceType.Methods which is used to set breakpoint.
-  // TODO(shertz) use command ReferenceType.MethodsWithGeneric instead
-  private static final List<DexVm> UNSUPPORTED_ART_VERSIONS = ImmutableList.of(DexVm.ART_4_4_4);
+  private static final List<DexVm> UNSUPPORTED_ART_VERSIONS = ImmutableList.of();
 
   private static final Path JDWP_JAR = ToolHelper
       .getJdwpTestsJarPath(ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm()));
@@ -579,16 +578,19 @@
         if (DEBUG_TESTS && debuggeeState.getLocation() != null) {
           // Dump location
           String classSig = getMirror().getClassSignature(debuggeeState.getLocation().classID);
-          String methodName = getMirror()
-              .getMethodName(debuggeeState.getLocation().classID,
+          String methodName = VmMirrorUtils
+              .getMethodName(getMirror(), debuggeeState.getLocation().classID,
                   debuggeeState.getLocation().methodID);
-          String methodSig = getMirror()
-              .getMethodSignature(debuggeeState.getLocation().classID,
+          String methodSig = VmMirrorUtils
+              .getMethodSignature(getMirror(), debuggeeState.getLocation().classID,
                   debuggeeState.getLocation().methodID);
-          System.out.println(String
-              .format("Suspended in %s#%s%s@0x%x (line=%d)", classSig, methodName, methodSig,
-                  Long.valueOf(debuggeeState.getLocation().index),
-                  Integer.valueOf(debuggeeState.getLineNumber())));
+          String msg = String
+              .format("Suspended in %s#%s%s@0x%x", classSig, methodName, methodSig,
+                  Long.valueOf(debuggeeState.getLocation().index));
+          if (debuggeeState.getLocation().index >= 0) {
+            msg += " (line " + debuggeeState.getLineNumber() + ")";
+          }
+          System.out.println(msg);
         }
 
         // Handle event.
@@ -611,7 +613,7 @@
               artCommandBuilder.appendArtOption("-Xcompiler-option");
               artCommandBuilder.appendArtOption("--debuggable");
             }
-            if (DEBUG_TESTS) {
+            if (DEBUG_TESTS && ToolHelper.getDexVm().isNewerThan(DexVm.ART_4_4_4)) {
               artCommandBuilder.appendArtOption("-verbose:jdwp");
             }
             setProperty("jpda.settings.debuggeeJavaPath", artCommandBuilder.build());
@@ -1450,7 +1452,7 @@
 
       @Override
       public boolean skipLocation(VmMirror mirror, Location location) {
-        // TODO(shertz) we also need to skip class loaders to act like IntelliJ.
+        // TODO(b/67225390) we also need to skip class loaders to act like IntelliJ.
         // Skip synthetic methods.
         if (isLambdaMethod(mirror, location)) {
           // Lambda methods are synthetic but we do want to stop there.
@@ -1478,15 +1480,10 @@
       private static boolean isSyntheticMethod(VmMirror mirror, Location location) {
         // We must gather the modifiers of the method. This is only possible using
         // ReferenceType.Methods command which gather information about all methods in a class.
-        ReplyPacket reply = mirror.getMethods(location.classID);
-        int methodsCount = reply.getNextValueAsInt();
-        for (int i = 0; i < methodsCount; ++i) {
-          long methodId = reply.getNextValueAsMethodID();
-          reply.getNextValueAsString();  // skip method name
-          reply.getNextValueAsString();  // skip method signature
-          int modifiers = reply.getNextValueAsInt();
-          if (methodId == location.methodID &&
-              ((modifiers & SYNTHETIC_FLAG) != 0)) {
+        Method[] methods = mirror.getMethods(location.classID);
+        for (Method method : methods) {
+          if (method.getMethodID() == location.methodID &&
+              ((method.getModBits() & SYNTHETIC_FLAG) != 0)) {
             return true;
           }
         }
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestExamples.java b/src/test/java/com/android/tools/r8/debug/DebugTestExamples.java
index 39091fb..91fed63 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestExamples.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestExamples.java
@@ -12,12 +12,15 @@
  */
 public class DebugTestExamples extends DebugTestBase {
 
+  public static final String SOURCE_FILE = "Arithmetic.java";
+  public static final String DEBUGGEE_CLASS = "Arithmetic";
+
   /**
    * Simple test that runs the debuggee until it exits.
    */
   @Test
   public void testRun() throws Throwable {
-    runDebugTest("Arithmetic", Collections.singletonList(run()));
+    runDebugTest(DEBUGGEE_CLASS, Collections.singletonList(run()));
   }
 
   /**
@@ -25,9 +28,10 @@
    */
   @Test
   public void testBreakpoint_Hit() throws Throwable {
-    runDebugTest("Arithmetic",
-        breakpoint("Arithmetic", "bitwiseInts"),
+    runDebugTest(DEBUGGEE_CLASS,
+        breakpoint(DEBUGGEE_CLASS, "bitwiseInts"),
         run(),
+        checkLine(SOURCE_FILE, 12),
         run());
   }
 
@@ -36,9 +40,10 @@
    */
   @Test
   public void testLocalsOnBreakpoint() throws Throwable {
-    runDebugTest("Arithmetic",
-        breakpoint("Arithmetic", "bitwiseInts"),
+    runDebugTest(DEBUGGEE_CLASS,
+        breakpoint(DEBUGGEE_CLASS, "bitwiseInts"),
         run(),
+        checkLine(SOURCE_FILE, 12),
         checkLocal("x", Value.createInt(12345)),
         checkLocal("y", Value.createInt(54321)),
         run());
@@ -49,9 +54,10 @@
    */
   @Test
   public void testLocalsOnBreakpointThenStep() throws Throwable {
-    runDebugTest("Arithmetic",
-        breakpoint("Arithmetic", "bitwiseInts"),
+    runDebugTest(DEBUGGEE_CLASS,
+        breakpoint(DEBUGGEE_CLASS, "bitwiseInts"),
         run(),
+        checkLine(SOURCE_FILE, 12),
         checkLocal("x", Value.createInt(12345)),
         checkLocal("y", Value.createInt(54321)),
         stepOver(),
diff --git a/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java b/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
index a7dcb60..03f011f 100644
--- a/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
@@ -4,9 +4,15 @@
 
 package com.android.tools.r8.debug;
 
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command;
+import com.android.tools.r8.debug.DebugTestBase.StepFilter.IntelliJStepFilter;
 import java.util.ArrayList;
 import java.util.List;
+import org.junit.Assume;
 import org.junit.Test;
 
 public class InterfaceMethodTest extends DebugTestBase {
@@ -15,6 +21,10 @@
 
   @Test
   public void testDefaultMethod() throws Throwable {
+    // TODO(b/67225390) Dalvik steps into class loader first.
+    Assume.assumeTrue("Dalvik suspends in class loader",
+        ToolHelper.getDexVm().isNewerThan(DexVm.ART_4_4_4));
+
     String debuggeeClass = "DebugInterfaceMethod";
     String parameterName = "msg";
     String localVariableName = "name";
@@ -30,6 +40,7 @@
       commands.add(stepInto());
     }
     commands.add(stepInto());
+    commands.add(checkLine(SOURCE_FILE, 9));
     // TODO(shertz) we should see the local variable this even when desugaring.
     if (supportsDefaultMethod()) {
       commands.add(checkLocal("this"));
diff --git a/src/test/java/com/android/tools/r8/debug/VmMirrorUtils.java b/src/test/java/com/android/tools/r8/debug/VmMirrorUtils.java
new file mode 100644
index 0000000..a3983da
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/VmMirrorUtils.java
@@ -0,0 +1,86 @@
+// 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.debug;
+
+import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands.ReferenceTypeCommandSet;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.Error;
+import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
+import org.apache.harmony.jpda.tests.framework.jdwp.VmMirror;
+
+/**
+ * Utils for JDWP mirror.
+ */
+public abstract class VmMirrorUtils {
+
+  private VmMirrorUtils() {
+  }
+
+  public static void checkReply(ReplyPacket replyPacket) {
+    checkReply(replyPacket, Error.NONE);
+  }
+
+  public static void checkReply(ReplyPacket replyPacket, int expectedErrorCode) {
+    if (replyPacket.getErrorCode() != expectedErrorCode) {
+      throw new AssertionError(
+          "Expected error code " + JDWPConstants.Error.getName(expectedErrorCode) + " ("
+              + expectedErrorCode + ") but received " + JDWPConstants.Error
+              .getName(expectedErrorCode) + " (" + expectedErrorCode + ")");
+    }
+
+  }
+
+  public static String getMethodName(VmMirror mirror, long classID, long methodID) {
+    CommandPacket packet = new CommandPacket(
+        JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
+        ReferenceTypeCommandSet.MethodsWithGenericCommand);
+    packet.setNextValueAsReferenceTypeID(classID);
+    ReplyPacket reply = mirror.performCommand(packet);
+    checkReply(reply);
+    int methodsCount = reply.getNextValueAsInt();
+    String result = null;
+    for (int i = 0; i < methodsCount; i++) {
+      long id = reply.getNextValueAsMethodID(); // skip method ID
+      String methodName = reply.getNextValueAsString();
+      reply.getNextValueAsString(); // skip signature
+      reply.getNextValueAsString(); // skip generic signature
+      reply.getNextValueAsInt(); // skip modifiers
+      if (id == methodID) {
+        result = methodName;
+      }
+    }
+    assert reply.isAllDataRead();
+    return result;
+  }
+
+  public static String getMethodSignature(VmMirror mirror, long classID, long methodID) {
+    CommandPacket command = new CommandPacket(
+        ReferenceTypeCommandSet.CommandSetID,
+        ReferenceTypeCommandSet.MethodsWithGenericCommand);
+    command.setNextValueAsReferenceTypeID(classID);
+    ReplyPacket reply = mirror.performCommand(command);
+    checkReply(reply);
+
+    int methods = reply.getNextValueAsInt();
+    for (int i = 0; i < methods; i++) {
+      long mID = reply.getNextValueAsMethodID();
+      reply.getNextValueAsString(); // skip method name
+      String methodSign = reply.getNextValueAsString();
+      reply.getNextValueAsString(); // skip generic signature
+      reply.getNextValueAsInt(); // skip modifiers
+      if (mID == methodID) {
+        String value = methodSign.replaceAll("/", ".");
+        int lastRoundBracketIndex = value.lastIndexOf(")");
+        return value.substring(0, lastRoundBracketIndex + 1);
+      }
+    }
+
+    assert reply.isAllDataRead();
+    return null;
+  }
+
+}
diff --git a/third_party/jdwp-tests.tar.gz.sha1 b/third_party/jdwp-tests.tar.gz.sha1
index 20b141e..dd998eb 100644
--- a/third_party/jdwp-tests.tar.gz.sha1
+++ b/third_party/jdwp-tests.tar.gz.sha1
@@ -1 +1 @@
-dd8dade9e939ae693b0b824d84268d09fa9f36cd
\ No newline at end of file
+c1f8da93dfe1b811904ee19a670fb5ed1a35766f
\ No newline at end of file