Merge "Add a test for writing files with shared classes."
diff --git a/.gitignore b/.gitignore
index 088d2b9..0090fb9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,6 +36,8 @@
 third_party/jdwp-tests
 third_party/kotlin.tar.gz
 third_party/kotlin
+third_party/shadow
+third_party/shadow.tar.gz
 third_party/photos/*
 !third_party/photos/*.sha1
 third_party/proguard/*
@@ -56,6 +58,7 @@
 gradle-app.setting
 gradlew
 gradlew.bat
+gradle/*
 #*#
 *~
 .#*
diff --git a/build.gradle b/build.gradle
index fdcc3bc..ac747c0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -184,44 +184,46 @@
 
 def cloudDependencies = [
         "tests"      : [
-                "2017-07-27/art.tar.gz",
-                "2016-12-19/art.tar.gz"
+                "2017-07-27/art",
+                "2016-12-19/art"
         ],
         "third_party": [
-                "android_jar/lib-v14.tar.gz",
-                "android_jar/lib-v19.tar.gz",
-                "android_jar/lib-v21.tar.gz",
-                "android_jar/lib-v24.tar.gz",
-                "android_jar/lib-v25.tar.gz",
-                "android_jar/lib-v26.tar.gz",
-                "proguard/proguard5.2.1.tar.gz",
-                "gradle/gradle.tar.gz",
-                "jdwp-tests.tar.gz",
-                "jasmin.tar.gz",
-                "jctf.tar.gz",
-                "kotlin.tar.gz",
-                "android_cts_baseline.tar.gz",
-                "shadow.tar.gz",
+                "android_jar/lib-v14",
+                "android_jar/lib-v19",
+                "android_jar/lib-v21",
+                "android_jar/lib-v24",
+                "android_jar/lib-v25",
+                "android_jar/lib-v26",
+                "proguard/proguard5.2.1",
+                "gradle/gradle",
+                "jdwp-tests",
+                "jasmin",
+                "jctf",
+                "kotlin",
+                "android_cts_baseline",
+                "shadow",
         ],
         // All dex-vms have a fixed OS of Linux, as they are only supported on Linux, and will be run in a Docker
         // container on other platforms where supported.
         "tools"      : [
-                "linux/art.tar.gz",
-                "linux/art-5.1.1.tar.gz",
-                "linux/art-6.0.1.tar.gz",
-                "linux/art-7.0.0.tar.gz",
-                "linux/dalvik.tar.gz",
-                "${osString}/dx.tar.gz",
+                "linux/art",
+                "linux/art-5.1.1",
+                "linux/art-6.0.1",
+                "linux/art-7.0.0",
+                "linux/dalvik",
+                "${osString}/dx",
         ]
 ]
 
 cloudDependencies.each { entry ->
     entry.value.each { entryFile ->
         task "download_deps_${entry.key}/${entryFile}"(type: Exec) {
-            def gzFile = "${entry.key}/${entryFile}"
+            def outputDir = "${entry.key}/${entryFile}"
+            def gzFile = "${outputDir}.tar.gz"
             def sha1File = "${gzFile}.sha1"
             inputs.file sha1File
             outputs.file gzFile
+            outputs.dir outputDir
             List<String> dlFromStorageArgs = ["-n", "-b", "r8-deps", "-u", "-s", "${sha1File}"]
             if (OperatingSystem.current().isWindows()) {
                 executable "download_from_google_storage.bat"
@@ -236,33 +238,35 @@
 
 def x20Dependencies = [
     "third_party": [
-        "gmail/gmail_android_170604.16.tar.gz",
-        "gmscore/v4.tar.gz",
-        "gmscore/v5.tar.gz",
-        "gmscore/v6.tar.gz",
-        "gmscore/v7.tar.gz",
-        "gmscore/v8.tar.gz",
-        "gmscore/gmscore_v9.tar.gz",
-        "gmscore/gmscore_v10.tar.gz",
-        "gmscore/latest.tar.gz",
-        "photos/2017-06-06.tar.gz",
-        "youtube/youtube.android_12.10.tar.gz",
-        "youtube/youtube.android_12.17.tar.gz",
-        "youtube/youtube.android_12.22.tar.gz",
-        "proguardsettings.tar.gz",
-        "proguard/proguard_internal_159423826.tar.gz",
-        "framework.tar.gz",
-        "goyt.tar.gz",
+        "gmail/gmail_android_170604.16",
+        "gmscore/v4",
+        "gmscore/v5",
+        "gmscore/v6",
+        "gmscore/v7",
+        "gmscore/v8",
+        "gmscore/gmscore_v9",
+        "gmscore/gmscore_v10",
+        "gmscore/latest",
+        "photos/2017-06-06",
+        "youtube/youtube.android_12.10",
+        "youtube/youtube.android_12.17",
+        "youtube/youtube.android_12.22",
+        "proguardsettings",
+        "proguard/proguard_internal_159423826",
+        "framework",
+        "goyt",
     ],
 ]
 
 x20Dependencies.each { entry ->
     entry.value.each { entryFile ->
         task "download_deps_${entry.key}/${entryFile}"(type: Exec) {
-            def gzFile = "${entry.key}/${entryFile}"
+            def outputDir = "${entry.key}/${entryFile}"
+            def gzFile = "${outputDir}.tar.gz"
             def sha1File = "${gzFile}.sha1"
             inputs.file sha1File
             outputs.file gzFile
+            outputs.dir outputDir
             executable "bash"
             args "-c", "tools/download_from_x20.py ${sha1File}"
         }
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
index 14e5288..a5a63e3 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -86,7 +86,7 @@
       // If this is the end of the block clear out the pending state.
       pendingLocals = null;
       pendingLocalChanges = false;
-    } else if (pc != emittedPc && !instruction.isNop()) {
+    } else if (pc != emittedPc && !instruction.isDebugLocalRead()) {
       // For non-exit / pc-advancing instructions emit any pending changes once possible.
       emitLocalChanges(pc);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
index 5089ca5..aebb9ed 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
@@ -126,14 +126,14 @@
   }
 
   @Override
-  public void removeOrReplaceByNop() {
+  public void removeOrReplaceByDebugLocalRead() {
     if (current == null) {
       throw new IllegalStateException();
     }
     if (current.getDebugValues().isEmpty()) {
       remove();
     } else {
-      replaceCurrentInstruction(new Nop());
+      replaceCurrentInstruction(new DebugLocalRead());
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/Nop.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
similarity index 76%
rename from src/main/java/com/android/tools/r8/ir/code/Nop.java
rename to src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
index dc378de..f0eb4a5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Nop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
@@ -10,25 +10,25 @@
 import com.android.tools.r8.ir.optimize.Inliner.Constraint;
 import com.android.tools.r8.utils.InternalOptions;
 
-public class Nop extends Instruction {
+public class DebugLocalRead extends Instruction {
 
-  public Nop() {
+  public DebugLocalRead() {
     super(null);
   }
 
   @Override
-  public boolean isNop() {
+  public boolean isDebugLocalRead() {
     return true;
   }
 
   @Override
-  public Nop asNop() {
+  public DebugLocalRead asDebugLocalRead() {
     return this;
   }
 
   @Override
   public void buildDex(DexBuilder builder) {
-    builder.addNop(this);
+    throw new Unreachable("Unexpected attempt to emit debug-local read.");
   }
 
   @Override
@@ -58,6 +58,8 @@
 
   @Override
   public boolean canBeDeadCode(IRCode code, InternalOptions options) {
-    return getDebugValues().isEmpty();
+    // Reads are never dead code.
+    // They should also have a non-empty set of debug values (see RegAlloc::computeDebugInfo)
+    return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionsIterator.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionsIterator.java
index 1390620..1bedf26 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionsIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionsIterator.java
@@ -53,7 +53,7 @@
   }
 
   @Override
-  public void removeOrReplaceByNop() {
-    instructionIterator.removeOrReplaceByNop();
+  public void removeOrReplaceByDebugLocalRead() {
+    instructionIterator.removeOrReplaceByDebugLocalRead();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 801c684..15291a5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -849,11 +849,11 @@
     return null;
   }
 
-  public boolean isNop() {
+  public boolean isDebugLocalRead() {
     return false;
   }
 
-  public Nop asNop() {
+  public DebugLocalRead asDebugLocalRead() {
     return null;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionIterator.java
index 56d81a2..2f2b3f1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionIterator.java
@@ -33,8 +33,8 @@
   void add(Instruction instruction);
 
   /**
-   * Safe removal function that will insert a Nop to take over the debug values if any are
-   * associated with the current instruction.
+   * Safe removal function that will insert a DebugLocalRead to take over the debug values if any
+   * are associated with the current instruction.
    */
-  void removeOrReplaceByNop();
+  void removeOrReplaceByDebugLocalRead();
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
index d8ea70b..92491c3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -60,10 +60,10 @@
   }
 
   /**
-   * Safe removal function that will insert a Nop to take over the debug values if any are
-   * associated with the current instruction.
+   * Safe removal function that will insert a DebugLocalRead to take over the debug values if any
+   * are associated with the current instruction.
    */
-  void removeOrReplaceByNop();
+  void removeOrReplaceByDebugLocalRead();
 
   /**
    * Remove the current instruction (aka the {@link Instruction} returned by the previous call to
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
index 3bdeefa..0d96df7 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -353,8 +353,7 @@
   }
 
   private static boolean isNopInstruction(com.android.tools.r8.ir.code.Instruction instruction) {
-    return instruction.isNop()
-        || instruction.isDebugLocalsChange()
+    return instruction.isDebugLocalsChange()
         || (instruction.isConstNumber() && !instruction.outValue().needsRegister());
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index f2dfce4..fc516e4 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -35,6 +35,7 @@
 import com.android.tools.r8.ir.code.ConstNumber;
 import com.android.tools.r8.ir.code.ConstString;
 import com.android.tools.r8.ir.code.ConstType;
+import com.android.tools.r8.ir.code.DebugLocalRead;
 import com.android.tools.r8.ir.code.DebugLocalUninitialized;
 import com.android.tools.r8.ir.code.DebugLocalWrite;
 import com.android.tools.r8.ir.code.DebugPosition;
@@ -59,7 +60,6 @@
 import com.android.tools.r8.ir.code.NewArrayEmpty;
 import com.android.tools.r8.ir.code.NewArrayFilledData;
 import com.android.tools.r8.ir.code.NewInstance;
-import com.android.tools.r8.ir.code.Nop;
 import com.android.tools.r8.ir.code.Not;
 import com.android.tools.r8.ir.code.NumberConversion;
 import com.android.tools.r8.ir.code.NumericType;
@@ -608,7 +608,7 @@
     // 1. The block is empty (eg, instructions from block entry until now materialized to nothing).
     // 2. The block is non-empty (and the last instruction does not define the local to start).
     if (currentBlock.getInstructions().isEmpty()) {
-      addInstruction(new Nop());
+      addInstruction(new DebugLocalRead());
     }
     Instruction instruction = currentBlock.getInstructions().getLast();
     assert instruction.outValue() != value;
@@ -630,7 +630,7 @@
     // 2. The block has an instruction not defining the local being ended.
     // 3. The block has an instruction defining the local being ended.
     if (currentBlock.getInstructions().isEmpty()) {
-      addInstruction(new Nop());
+      addInstruction(new DebugLocalRead());
     }
     Instruction instruction = currentBlock.getInstructions().getLast();
     if (instruction.outValue() != value) {
@@ -645,7 +645,7 @@
     if (instruction.isDebugLocalWrite()) {
       DebugLocalWrite write = instruction.asDebugLocalWrite();
       currentBlock.replaceCurrentDefinitions(value, write.src());
-      currentBlock.listIterator(write).removeOrReplaceByNop();
+      currentBlock.listIterator(write).removeOrReplaceByDebugLocalRead();
     } else {
       instruction.outValue().clearLocalInfo();
     }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarState.java b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
index 828ff10..f11af49 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
@@ -176,7 +176,7 @@
   // without conflating locals that are shared among different types. This issue arises because a
   // debugging range can be larger than the definite-assignment scope of a local (eg, a local
   // introduced in an unscoped switch case). To ensure that the SSA graph is valid we must introduce
-  // the local before inserting any DebugLocalReads (we do so in the method prelude, but that can
+  // the local before inserting any DebugLocalRead (we do so in the method prelude, but that can
   // potentially lead to phi functions merging locals of different move-types. Thus we allocate
   // registers from the three distinct spaces.
   private final int localsSize;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 7a8a4c3..ca307f4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1088,7 +1088,7 @@
             user -> user.isCheckCast()
                 && user.asCheckCast().getType().isSubtypeOf(checkCast.getType(), appInfo))) {
           checkCast.outValue().replaceUsers(checkCast.inValues().get(0));
-          it.removeOrReplaceByNop();
+          it.removeOrReplaceByDebugLocalRead();
         }
       }
     }
@@ -1369,7 +1369,8 @@
       InstructionListIterator it = block.listIterator();
       while (it.hasNext()) {
         Instruction instruction = it.next();
-        if (!isPrimitiveOrStringNewArrayWithPositiveSize(instruction)) {
+        if (instruction.getLocalInfo() != null
+            || !isPrimitiveOrStringNewArrayWithPositiveSize(instruction)) {
           continue;
         }
         NewArrayEmpty newArray = instruction.asNewArrayEmpty();
@@ -1507,7 +1508,7 @@
         write.outValue().replaceUsers(phi);
         // Safely remove the write.
         // TODO(zerny): Once phis become instructions, move debug values there instead of a nop.
-        iterator.removeOrReplaceByNop();
+        iterator.removeOrReplaceByDebugLocalRead();
         return;
       }
     }
@@ -1671,6 +1672,10 @@
             || instruction.isUnop()
             || instruction.isInstanceOf()
             || instruction.isCheckCast()) {
+          if (instruction.getLocalInfo() != null || instruction.hasInValueWithLocalInfo()) {
+            // If the instruction has input or output values then it is not safe to share it.
+            continue;
+          }
           List<Value> candidates = instructionToValue.get(equivalence.wrap(instruction));
           boolean eliminated = false;
           if (candidates.size() > 0) {
@@ -1679,12 +1684,12 @@
                   shareCatchHandlers(instruction, candidate.definition)) {
                 instruction.outValue().replaceUsers(candidate);
                 eliminated = true;
-                iterator.removeOrReplaceByNop();
+                iterator.removeOrReplaceByDebugLocalRead();
                 break;  // Don't try any more candidates.
               }
             }
           }
-          if (!eliminated && !instruction.hasInValueWithLocalInfo()) {
+          if (!eliminated) {
             instructionToValue.put(equivalence.wrap(instruction), instruction.outValue());
           }
         }
@@ -1928,13 +1933,13 @@
           DexMethod invokedMethod = current.asInvokeMethod().getInvokedMethod();
           if (matchesMethodOfThrowable(invokedMethod, throwableMethods.addSuppressed)) {
             // Remove Throwable::addSuppressed(Throwable) call.
-            iterator.removeOrReplaceByNop();
+            iterator.removeOrReplaceByDebugLocalRead();
           } else if (matchesMethodOfThrowable(invokedMethod, throwableMethods.getSuppressed)) {
             Value destValue = current.outValue();
             if (destValue == null) {
               // If the result of the call was not used we don't create
               // an empty array and just remove the call.
-              iterator.removeOrReplaceByNop();
+              iterator.removeOrReplaceByDebugLocalRead();
               continue;
             }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
index ebf69b5..2790c03 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
@@ -120,7 +120,7 @@
       // All users will be removed for this instruction. Eagerly clear them so further inspection
       // of this instruction during dead code elimination will terminate here.
       outValue.clearUsers();
-      iterator.removeOrReplaceByNop();
+      iterator.removeOrReplaceByDebugLocalRead();
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index a8b3972..7facea6 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -324,6 +324,12 @@
       int spillCount = 0;
       while (instructionIterator.hasNext()) {
         Instruction instruction = instructionIterator.next();
+        if (instruction.isDebugLocalRead()) {
+          // Remove debug local reads now that local liveness is computed.
+          assert !instruction.getDebugValues().isEmpty();
+          instruction.clearDebugValues();
+          instructionIterator.remove();
+        }
         int index = instruction.getNumber();
         if (index == -1) {
           spillCount++;
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/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
index 8ab2de5..c8ea3bb 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -74,7 +74,7 @@
     }
 
     @Override
-    public void removeOrReplaceByNop() {
+    public void removeOrReplaceByDebugLocalRead() {
       remove();
     }
 
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
diff --git a/tools/test.py b/tools/test.py
index 9774207..7818915 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -84,9 +84,6 @@
 
 def Main():
   (options, args) = ParseOptions()
-  if len(args) > 1:
-    print("test.py takes at most one argument, the pattern for tests to run")
-    return -1
 
   gradle_args = []
   # Set all necessary Gradle properties and options first.
@@ -131,10 +128,10 @@
   # Add Gradle tasks
   gradle_args.append('cleanTest')
   gradle_args.append('test')
-  if len(args) > 0:
-    # Test filtering. Must always follow the 'test' task.
+  # Test filtering. Must always follow the 'test' task.
+  for testFilter in args:
     gradle_args.append('--tests')
-    gradle_args.append(args[0])
+    gradle_args.append(testFilter)
   if options.with_code_coverage:
     # Create Jacoco report after tests.
     gradle_args.append('jacocoTestReport')