Merge "Replace the use of switchmaps with direct use of ordinal."
diff --git a/src/test/debugTestResources/Bridges.java b/src/test/debugTestResources/Bridges.java
new file mode 100644
index 0000000..e1a4380
--- /dev/null
+++ b/src/test/debugTestResources/Bridges.java
@@ -0,0 +1,27 @@
+// 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.
+
+public class Bridges {
+
+  interface GenericInterface<T> {
+
+    void get(T t);
+  }
+
+  static class StringImpl implements GenericInterface<String> {
+
+    @Override
+    public void get(String s) {
+      System.out.println(s);
+    }
+  }
+
+  public static void testGenericBridge(GenericInterface<String> obj) {
+    obj.get("Foo");
+  }
+
+  public static void main(String[] args) {
+    testGenericBridge(new StringImpl());
+  }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
index 7f9ecb0..49c0837 100644
--- a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
+++ b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
@@ -6,6 +6,7 @@
 
 import static com.android.tools.r8.TestCondition.D8_COMPILER;
 import static com.android.tools.r8.TestCondition.R8_COMPILER;
+import static com.android.tools.r8.TestCondition.R8DEBUG_AFTER_D8_COMPILER;
 import static com.android.tools.r8.TestCondition.any;
 import static com.android.tools.r8.TestCondition.match;
 import static com.android.tools.r8.TestCondition.runtimes;
@@ -4653,6 +4654,14 @@
           // 1) t04
           // java.lang.AssertionError
 
+          .put("lang.reflect.Field.getLjava_lang_Object.Field_get_A04", match(R8DEBUG_AFTER_D8_COMPILER))
+          // 1) t02
+          // java.lang.AssertionError: expected:<9223372036854775807> but was:<72057594037927935>
+
+          .put("lang.reflect.Field.getLongLjava_lang_Object.Field_getLong_A04", match(R8DEBUG_AFTER_D8_COMPILER))
+          // 1)
+          // java.lang.AssertionError: expected:<9223372036854775807> but was:<72057594037927935>
+
           .build(); // end of failuresToTriage
 
   public static final Multimap<String, TestCondition> flakyWithArt =
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index b026dcd..5c5975c 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.utils.ArtErrorParser;
 import com.android.tools.r8.utils.ArtErrorParser.ArtErrorInfo;
 import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.JarBuilder;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.OffOrAuto;
@@ -25,10 +26,10 @@
 import com.google.common.collect.Multimap;
 import com.google.common.collect.ObjectArrays;
 import com.google.common.collect.Sets;
-import com.google.common.io.Files;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
@@ -54,7 +55,6 @@
 public abstract class R8RunArtTestsTest {
 
   private static final boolean DEX_COMPARE_WITH_DEX_REFERENCE_ON_FAILURE = true;
-  private static String[] D8_EXTRA_ARGS = {"--debug"};
 
   private final String name;
   private final DexTool toolchain;
@@ -70,7 +70,8 @@
 
   public enum CompilerUnderTest {
     D8,
-    R8
+    R8,
+    R8DEBUG_AFTER_D8 // refers to the R8/debug step but implies a previous D8 step as well
   }
 
   private static final String ART_TESTS_DIR = "tests/art";
@@ -937,13 +938,23 @@
   private void executeCompilerUnderTest(
       CompilerUnderTest compilerUnderTest, Collection<String> fileNames, String resultPath)
       throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
-    executeCompilerUnderTest(compilerUnderTest, fileNames, resultPath, null);
+    executeCompilerUnderTest(compilerUnderTest, fileNames, resultPath, null, null);
   }
 
   private void executeCompilerUnderTest(
       CompilerUnderTest compilerUnderTest,
       Collection<String> fileNames,
       String resultPath,
+      CompilationMode compilationMode)
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+    executeCompilerUnderTest(compilerUnderTest, fileNames, resultPath, compilationMode, null);
+  }
+
+  private void executeCompilerUnderTest(
+      CompilerUnderTest compilerUnderTest,
+      Collection<String> fileNames,
+      String resultPath,
+      CompilationMode mode,
       String keepRulesFile)
       throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
     switch (compilerUnderTest) {
@@ -951,7 +962,7 @@
         assert keepRulesFile == null : "Keep-rules file specified for D8.";
         D8Command.Builder builder =
             D8Command.builder()
-                .setMode(CompilationMode.DEBUG)
+                .setMode(mode == null ? CompilationMode.DEBUG : mode)
                 .addProgramFiles(ListUtils.map(fileNames, Paths::get));
         Integer minSdkVersion = needMinSdkVersion.get(name);
         if (minSdkVersion != null) {
@@ -964,7 +975,7 @@
       case R8: {
         R8Command.Builder builder =
             R8Command.builder()
-                .setMode(CompilationMode.RELEASE)
+                .setMode(mode == null ? CompilationMode.RELEASE : mode)
                 .setOutputPath(Paths.get(resultPath))
                 .addProgramFiles(ListUtils.map(fileNames, Paths::get))
                 .setIgnoreMissingClasses(true);
@@ -1045,10 +1056,15 @@
 
     DexVm dexVm = ToolHelper.getDexVm();
 
-    File resultDir = temp.getRoot();
+    CompilerUnderTest firstCompilerUnderTest =
+        compilerUnderTest == CompilerUnderTest.R8DEBUG_AFTER_D8
+            ? CompilerUnderTest.D8
+            : compilerUnderTest;
+
+    File resultDir = temp.newFolder(firstCompilerUnderTest.toString().toLowerCase() + "-output");
 
     JctfTestSpecifications.Outcome expectedOutcome = JctfTestSpecifications
-        .getExpectedOutcome(name, compilerUnderTest, dexVm);
+        .getExpectedOutcome(name, firstCompilerUnderTest, dexVm);
     TestSpecification specification = new TestSpecification(name, DexTool.NONE, resultDir,
         expectedOutcome == JctfTestSpecifications.Outcome.TIMEOUTS_WITH_ART
             || expectedOutcome == JctfTestSpecifications.Outcome.FLAKY_WITH_ART,
@@ -1116,7 +1132,53 @@
     for (File f : allClassFiles) {
       fileNames.add(f.getCanonicalPath());
     }
-    executeCompilerUnderTest(compilerUnderTest, fileNames, resultDir.getCanonicalPath());
+
+    runJctfTestDoRunOnArt(fileNames,
+        specification,
+        firstCompilerUnderTest,
+        fullClassName,
+        null,
+        dexVm,
+        resultDir);
+
+    // second pass if D8_R8Debug
+    if (compilerUnderTest == CompilerUnderTest.R8DEBUG_AFTER_D8) {
+      List<String> d8OutputFileNames =
+          Files.list(resultDir.toPath())
+              .filter(FileUtils::isDexFile)
+              .map(Path::toString)
+              .collect(Collectors.toList());
+      File r8ResultDir = temp.newFolder("r8-output");
+      expectedOutcome = JctfTestSpecifications
+          .getExpectedOutcome(name, CompilerUnderTest.R8DEBUG_AFTER_D8, dexVm);
+      specification = new TestSpecification(name, DexTool.DX, r8ResultDir,
+          expectedOutcome == JctfTestSpecifications.Outcome.TIMEOUTS_WITH_ART
+              || expectedOutcome == JctfTestSpecifications.Outcome.FLAKY_WITH_ART,
+          expectedOutcome == JctfTestSpecifications.Outcome.FAILS_WITH_ART);
+      if (specification.skipTest) {
+        return;
+      }
+      runJctfTestDoRunOnArt(
+          d8OutputFileNames,
+          specification,
+          CompilerUnderTest.R8,
+          fullClassName,
+          CompilationMode.DEBUG,
+          dexVm,
+          r8ResultDir);
+    }
+  }
+
+  private void runJctfTestDoRunOnArt(
+      Collection<String> fileNames,
+      TestSpecification specification,
+      CompilerUnderTest compilerUnderTest,
+      String fullClassName,
+      CompilationMode mode,
+      DexVm dexVm,
+      File resultDir)
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+    executeCompilerUnderTest(compilerUnderTest, fileNames, resultDir.getAbsolutePath(), mode);
 
     if (!ToolHelper.artSupported()) {
       return;
@@ -1196,7 +1258,7 @@
     if (toolchain == DexTool.NONE) {
       File classes = new File(specification.directory, "classes");
       inputFiles =
-          Files.fileTreeTraverser().breadthFirstTraversal(classes).filter(
+          com.google.common.io.Files.fileTreeTraverser().breadthFirstTraversal(classes).filter(
               (File f) -> !f.isDirectory()).toArray(File.class);
       File smali = new File(specification.directory, "smali");
       if (smali.exists()) {
@@ -1207,7 +1269,7 @@
       File classes2 = new File(specification.directory, "classes2");
       if (classes2.exists()) {
         inputFiles = ObjectArrays.concat(inputFiles,
-            Files.fileTreeTraverser().breadthFirstTraversal(classes2).filter(
+            com.google.common.io.Files.fileTreeTraverser().breadthFirstTraversal(classes2).filter(
                 (File f) -> !f.isDirectory()).toArray(File.class), File.class);
       }
     } else {
@@ -1258,7 +1320,7 @@
       }
 
       File expectedFile = specification.resolveFile("expected.txt");
-      String expected = Files.toString(expectedFile, Charsets.UTF_8);
+      String expected = com.google.common.io.Files.toString(expectedFile, Charsets.UTF_8);
       if (specification.failsWithArt) {
         thrown.expect(AssertionError.class);
       }
@@ -1281,7 +1343,7 @@
       if (checkCommand.exists()) {
         // Run the Art test custom check command.
         File actualFile = temp.newFile();
-        Files.asByteSink(actualFile).write(output.getBytes(Charsets.UTF_8));
+        com.google.common.io.Files.asByteSink(actualFile).write(output.getBytes(Charsets.UTF_8));
         ProcessBuilder processBuilder = new ProcessBuilder();
         processBuilder.command(
             specification.resolveFile("check").toString(), expectedFile.toString(),
diff --git a/src/test/java/com/android/tools/r8/TestCondition.java b/src/test/java/com/android/tools/r8/TestCondition.java
index ef2eac3..5ffd779 100644
--- a/src/test/java/com/android/tools/r8/TestCondition.java
+++ b/src/test/java/com/android/tools/r8/TestCondition.java
@@ -40,6 +40,8 @@
 
   public static final CompilerSet D8_COMPILER = compilers(CompilerUnderTest.D8);
   public static final CompilerSet R8_COMPILER = compilers(CompilerUnderTest.R8);
+  public static final CompilerSet R8DEBUG_AFTER_D8_COMPILER =
+      compilers(CompilerUnderTest.R8DEBUG_AFTER_D8);
 
   private static final ToolSet ANY_TOOL = new ToolSet(EnumSet.allOf(DexTool.class));
   private static final CompilerSet ANY_COMPILER =
@@ -107,7 +109,13 @@
   }
 
   public boolean test(DexTool dexTool, CompilerUnderTest compilerUnderTest, DexVm dexVm) {
-    return dexTools.contains(dexTool) && compilers.contains(compilerUnderTest)
+    // R8DEBUG_AFTER_D8 will be set in the R8 phase of the D8-then-R8 tests. So R8DEBUG_AFTER_D8
+    // must match both with plain R8 and itself.
+    boolean compilerMatches = compilers.contains(compilerUnderTest)
+        || (compilerUnderTest == CompilerUnderTest.R8DEBUG_AFTER_D8
+            && compilers.contains(CompilerUnderTest.R8));
+    return dexTools.contains(dexTool)
+        && compilerMatches
         && dexVms.contains(dexVm);
   }
 }
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 8f2e9ce..fa7443c 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 java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayDeque;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -29,12 +30,15 @@
 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.ReferenceTypeCommandSet;
 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands.StackFrameCommandSet;
 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.JDWPConstants.EventKind;
 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.StepDepth;
 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.StepSize;
 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.ParsedEvent;
 import org.apache.harmony.jpda.tests.framework.jdwp.ParsedEvent.EventThread;
@@ -73,14 +77,15 @@
   // Set to true to enable verbose logs
   private static final boolean DEBUG_TESTS = false;
 
-  private static final List<DexVm> UNSUPPORTED_ART_VERSIONS = ImmutableList.of(
+  private static final List<DexVm> UNSUPPORTED_ART_VERSIONS = ImmutableList.<DexVm>builder()
       // Dalvik does not support command ReferenceType.Methods which is used to set breakpoint.
       // TODO(shertz) use command ReferenceType.MethodsWithGeneric instead
-      DexVm.ART_4_4_4,
+      .add(DexVm.ART_4_4_4)
       // Older runtimes fail on buildbot
       // TODO(shertz) re-enable once issue is solved
-      DexVm.ART_5_1_1,
-      DexVm.ART_6_0_1);
+      .add(DexVm.ART_5_1_1)
+      .add(DexVm.ART_6_0_1)
+      .build();
 
   private static final Path JDWP_JAR = ToolHelper
       .getJdwpTestsJarPath(ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm()));
@@ -200,7 +205,12 @@
   }
 
   protected final JUnit3Wrapper.Command breakpoint(String className, String methodName) {
-    return new JUnit3Wrapper.Command.BreakpointCommand(className, methodName);
+    return breakpoint(className, methodName, null);
+  }
+
+  protected final JUnit3Wrapper.Command breakpoint(String className, String methodName,
+      String methodSignature) {
+    return new JUnit3Wrapper.Command.BreakpointCommand(className, methodName, methodSignature);
   }
 
   protected final JUnit3Wrapper.Command stepOver() {
@@ -244,12 +254,27 @@
     return inspect(t -> Assert.assertTrue(t.getLocalNames().isEmpty()));
   }
 
-  protected final JUnit3Wrapper.Command checkLine(int line) {
-    return inspect(t -> t.checkLine(line));
+  protected final JUnit3Wrapper.Command checkLine(String sourceFile, int line) {
+    return inspect(t -> {
+      Assert.assertEquals(sourceFile, t.getCurrentSourceFile());
+      Assert.assertEquals(line, t.getCurrentLineNumber());
+    });
   }
 
   protected final JUnit3Wrapper.Command checkMethod(String className, String methodName) {
-    return inspect(t -> t.checkMethod(className, methodName));
+    return checkMethod(className, methodName, null);
+  }
+
+  protected final JUnit3Wrapper.Command checkMethod(String className, String methodName,
+      String methodSignature) {
+    return inspect(t -> {
+      Assert.assertEquals("Incorrect class name", className, t.getCurrentClassName());
+      Assert.assertEquals("Incorrect method name", methodName, t.getCurrentMethodName());
+      if (methodSignature != null) {
+        Assert.assertEquals("Incorrect method signature", methodSignature,
+            t.getCurrentMethodSignature());
+      }
+    });
   }
 
   protected final JUnit3Wrapper.Command inspect(Consumer<JUnit3Wrapper.DebuggeeState> inspector) {
@@ -477,26 +502,91 @@
         Assert.assertEquals(expectedValue, localValue);
       }
 
-      public void checkLine(int line) {
-        Location location = getLocation();
-        int currentLine = getMirror()
-            .getLineNumber(location.classID, location.methodID, location.index);
-        Assert.assertEquals(line, currentLine);
+      public int getCurrentLineNumber() {
+        ReplyPacket reply = getMirror().getLineTable(location.classID, location.methodID);
+        if (reply.getErrorCode() != 0) {
+          return -1;
+        }
+
+        long startCodeIndex = reply.getNextValueAsLong();
+        long endCodeIndex = reply.getNextValueAsLong();
+        int lines = reply.getNextValueAsInt();
+        int line = -1;
+        long previousLineCodeIndex = -1;
+        for (int i = 0; i < lines; ++i) {
+          long currentLineCodeIndex = reply.getNextValueAsLong();
+          int currentLineNumber = reply.getNextValueAsInt();
+
+          // Code indices are in ascending order.
+          assert currentLineCodeIndex >= startCodeIndex;
+          assert currentLineCodeIndex <= endCodeIndex;
+          assert currentLineCodeIndex > previousLineCodeIndex;
+          previousLineCodeIndex = currentLineCodeIndex;
+
+          if (location.index >= currentLineCodeIndex) {
+            line = currentLineNumber;
+          } else {
+            break;
+          }
+        }
+
+        return line;
+      }
+
+      public String getCurrentSourceFile() {
+        CommandPacket sourceFileCommand = new CommandPacket(
+            JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
+            JDWPCommands.ReferenceTypeCommandSet.SourceFileCommand);
+        sourceFileCommand.setNextValueAsReferenceTypeID(location.classID);
+        ReplyPacket replyPacket = getMirror().performCommand(sourceFileCommand);
+        if (replyPacket.getErrorCode() != 0) {
+          return null;
+        } else {
+          return replyPacket.getNextValueAsString();
+        }
       }
 
       public List<String> getLocalNames() {
         return getVariablesAt(location).stream().map(v -> v.getName()).collect(Collectors.toList());
       }
 
-      public void checkMethod(String className, String methodName) {
-        String currentClassSig = getMirror().getClassSignature(location.classID);
-        assert currentClassSig.charAt(0) == 'L';
-        String currentClassName = currentClassSig.substring(1, currentClassSig.length() - 1)
-            .replace('/', '.');
-        Assert.assertEquals("Incorrect class name", className, currentClassName);
+      public String getCurrentClassName() {
+        String classSignature = getCurrentClassSignature();
+        assert classSignature.charAt(0) == 'L';
+        // Remove leading 'L' and trailing ';'
+        classSignature = classSignature.substring(1, classSignature.length() - 1);
+        // Return fully qualified name
+        return classSignature.replace('/', '.');
+      }
 
-        String currentMethodName = getMirror().getMethodName(location.classID, location.methodID);
-        Assert.assertEquals("Incorrect method name", methodName, currentMethodName);
+      public String getCurrentClassSignature() {
+        return getMirror().getClassSignature(location.classID);
+      }
+
+      public String getCurrentMethodName() {
+        return getMirror().getMethodName(location.classID, location.methodID);
+      }
+
+      public String getCurrentMethodSignature() {
+        CommandPacket command = new CommandPacket(ReferenceTypeCommandSet.CommandSetID,
+            ReferenceTypeCommandSet.MethodsWithGenericCommand);
+        command.setNextValueAsReferenceTypeID(location.classID);
+
+        ReplyPacket reply = getMirror().performCommand(command);
+        assert reply.getErrorCode() == Error.NONE;
+        int methods = reply.getNextValueAsInt();
+
+        for (int i = 0; i < methods; ++i) {
+          long methodId = reply.getNextValueAsMethodID();
+          reply.getNextValueAsString(); // skip name
+          String methodSignature = reply.getNextValueAsString();
+          reply.getNextValueAsString(); // skip generic signature
+          reply.getNextValueAsInt();  // skip modifiers
+          if (methodId == location.methodID) {
+            return methodSignature;
+          }
+        }
+        throw new AssertionError("No method info for the current location");
       }
     }
 
@@ -564,7 +654,14 @@
     }
 
     private boolean installBreakpoint(BreakpointInfo breakpointInfo) {
-      final long classId = getMirror().getClassID(getClassSignature(breakpointInfo.className));
+      String classSignature = getClassSignature(breakpointInfo.className);
+      byte typeTag = TypeTag.CLASS;
+      long classId = getMirror().getClassID(classSignature);
+      if (classId == -1) {
+        // Is it an interface ?
+        classId = getMirror().getInterfaceID(classSignature);
+        typeTag = TypeTag.INTERFACE;
+      }
       if (classId == -1) {
         // The class is not ready yet. Request a CLASS_PREPARE to delay the installation of the
         // breakpoint.
@@ -575,14 +672,95 @@
             new ClassPrepareHandler(breakpointInfo, classPrepareRequestId));
         return false;
       } else {
-        int breakpointId = getMirror()
-            .setBreakpointAtMethodBegin(classId, breakpointInfo.methodName);
+        // Find the method.
+        long breakpointMethodId = findMethod(classId, breakpointInfo.methodName,
+            breakpointInfo.methodSignature);
+        long index = getMethodFirstCodeIndex(classId, breakpointMethodId);
+        Assert.assertTrue("No code in method", index >= 0);
+        // Install the breakpoint.
+        ReplyPacket replyPacket = getMirror()
+            .setBreakpoint(new Location(typeTag, classId, breakpointMethodId, index),
+                SuspendPolicy.ALL);
+        checkReplyPacket(replyPacket, "Breakpoint");
+        int breakpointId = replyPacket.getNextValueAsInt();
         // Nothing to do on breakpoint
         events.put(Integer.valueOf(breakpointId), new DefaultEventHandler());
         return true;
       }
     }
 
+    private long findMethod(long classId, String methodName, String methodSignature) {
+      class MethodInfo {
+
+        final long methodId;
+        final String methodName;
+        final String methodSignature;
+
+        MethodInfo(long methodId, String methodName, String methodSignature) {
+          this.methodId = methodId;
+          this.methodName = methodName;
+          this.methodSignature = methodSignature;
+        }
+      }
+
+      boolean withGenericSignature = true;
+      CommandPacket commandPacket = new CommandPacket(ReferenceTypeCommandSet.CommandSetID,
+          ReferenceTypeCommandSet.MethodsWithGenericCommand);
+      commandPacket.setNextValueAsReferenceTypeID(classId);
+      ReplyPacket replyPacket = getMirror().performCommand(commandPacket);
+      if (replyPacket.getErrorCode() != Error.NONE) {
+        // Retry with older command ReferenceType.Methods
+        withGenericSignature = false;
+        commandPacket.setCommand(ReferenceTypeCommandSet.MethodsCommand);
+        replyPacket = getMirror().performCommand(commandPacket);
+        assert replyPacket.getErrorCode() == Error.NONE;
+      }
+
+      int methodsCount = replyPacket.getNextValueAsInt();
+      List<MethodInfo> methodInfos = new ArrayList<>(methodsCount);
+      for (int i = 0; i < methodsCount; ++i) {
+        long currentMethodId = replyPacket.getNextValueAsMethodID();
+        String currentMethodName = replyPacket.getNextValueAsString();
+        String currentMethodSignature = replyPacket.getNextValueAsString();
+        if (withGenericSignature) {
+          replyPacket.getNextValueAsString(); // skip generic signature
+        }
+        replyPacket.getNextValueAsInt(); // skip modifiers
+        methodInfos
+            .add(new MethodInfo(currentMethodId, currentMethodName, currentMethodSignature));
+      }
+      Assert.assertTrue(replyPacket.isAllDataRead());
+
+      // Only keep methods with the expected name.
+      methodInfos = methodInfos.stream()
+          .filter(m -> m.methodName.equals(methodName)).collect(
+              Collectors.toList());
+      if (methodSignature != null) {
+        methodInfos = methodInfos.stream()
+            .filter(m -> methodSignature.equals(m.methodSignature)).collect(
+                Collectors.toList());
+      }
+      Assert.assertFalse("No method found", methodInfos.isEmpty());
+      // There must be only one matching method
+      Assert.assertEquals("More than 1 method found: please specify a signature", 1,
+          methodInfos.size());
+      return methodInfos.get(0).methodId;
+    }
+
+    private long getMethodFirstCodeIndex(long classId, long breakpointMethodId) {
+      ReplyPacket replyPacket = getMirror().getLineTable(classId, breakpointMethodId);
+      checkReplyPacket(replyPacket, "Failed to get method line table");
+      replyPacket.getNextValueAsLong(); // start
+      replyPacket.getNextValueAsLong(); // end
+      int linesCount = replyPacket.getNextValueAsInt();
+      if (linesCount == 0) {
+        return -1;
+      } else {
+        // Read only the 1st line because code indices are in ascending order
+        return replyPacket.getNextValueAsLong();
+      }
+    }
+
     //
     // Command processing
     //
@@ -603,22 +781,24 @@
         }
       }
 
-      // TODO(shertz) add method signature support (when multiple methods have the same name)
       class BreakpointCommand implements Command {
 
         private final String className;
         private final String methodName;
+        private final String methodSignature;
 
-        public BreakpointCommand(String className, String methodName) {
+        public BreakpointCommand(String className, String methodName,
+            String methodSignature) {
           assert className != null;
           assert methodName != null;
           this.className = className;
           this.methodName = methodName;
+          this.methodSignature = methodSignature;
         }
 
         @Override
         public void perform(JUnit3Wrapper testBase) {
-          testBase.installBreakpoint(new BreakpointInfo(className, methodName));
+          testBase.installBreakpoint(new BreakpointInfo(className, methodName, methodSignature));
         }
 
         @Override
@@ -740,10 +920,12 @@
 
       private final String className;
       private final String methodName;
+      private final String methodSignature;
 
-      private BreakpointInfo(String className, String methodName) {
+      private BreakpointInfo(String className, String methodName, String methodSignature) {
         this.className = className;
         this.methodName = methodName;
+        this.methodSignature = methodSignature;
       }
     }
 
diff --git a/src/test/java/com/android/tools/r8/debug/DefaultMethodTest.java b/src/test/java/com/android/tools/r8/debug/DefaultMethodTest.java
index ea116a7..e0eb5ea 100644
--- a/src/test/java/com/android/tools/r8/debug/DefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/debug/DefaultMethodTest.java
@@ -11,6 +11,8 @@
 
 public class DefaultMethodTest extends DebugTestBase {
 
+  private static final String SOURCE_FILE = "DebugDefaultMethod.java";
+
   @Test
   public void testDefaultMethod() throws Throwable {
     String debuggeeClass = "DebugDefaultMethod";
@@ -21,7 +23,7 @@
     commands.add(breakpoint(debuggeeClass, "testDefaultMethod"));
     commands.add(run());
     commands.add(checkMethod(debuggeeClass, "testDefaultMethod"));
-    commands.add(checkLine(27));
+    commands.add(checkLine(SOURCE_FILE, 27));
     if (!supportsDefaultMethod()) {
       // We desugared default method. This means we're going to step through an extra (forward)
       // method first.
@@ -50,7 +52,7 @@
     commands.add(run());
     commands.add(run() /* resume after 1st breakpoint */);
     commands.add(checkMethod(debuggeeClass, "testDefaultMethod"));
-    commands.add(checkLine(27));
+    commands.add(checkLine(SOURCE_FILE, 27));
     commands.add(stepInto());
     commands.add(checkMethod("DebugDefaultMethod$OverrideImpl", "doSomething"));
     commands.add(checkLocal(parameterName));
@@ -61,5 +63,4 @@
 
     runDebugTestJava8(debuggeeClass, commands);
   }
-
 }
diff --git a/src/test/java/com/android/tools/r8/debug/ExceptionTest.java b/src/test/java/com/android/tools/r8/debug/ExceptionTest.java
index 5f15f8f..3d4202b 100644
--- a/src/test/java/com/android/tools/r8/debug/ExceptionTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ExceptionTest.java
@@ -10,6 +10,8 @@
  */
 public class ExceptionTest extends DebugTestBase {
 
+  public static final String SOURCE_FILE = "Exceptions.java";
+
   @Test
   public void testStepOnCatch() throws Throwable {
     int catchLine;
@@ -24,9 +26,9 @@
     runDebugTest("Exceptions",
         breakpoint("Exceptions", "catchException"),
         run(),
-        checkLine(9), // line of the method call throwing the exception
+        checkLine(SOURCE_FILE, 9), // line of the method call throwing the exception
         stepOver(),
-        checkLine(catchLine), // line of the catch declaration
+        checkLine(SOURCE_FILE, catchLine), // line of the catch declaration
         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 467ae09..52d9c70 100644
--- a/src/test/java/com/android/tools/r8/debug/LambdaTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LambdaTest.java
@@ -8,6 +8,8 @@
 
 public class LambdaTest extends DebugTestBase {
 
+  public static final String SOURCE_FILE = "DebugLambda.java";
+
   @Test
   public void testLambdaDebugging() throws Throwable {
     String debuggeeClass = "DebugLambda";
@@ -17,9 +19,9 @@
         breakpoint(debuggeeClass, initialMethodName),
         run(),
         checkMethod(debuggeeClass, initialMethodName),
-        checkLine(12),
+        checkLine(SOURCE_FILE, 12),
         stepInto(INTELLIJ_FILTER),
-        checkLine(16),
+        checkLine(SOURCE_FILE, 16),
         run());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/debug/LocalsTest.java b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
index 0d72ab9..6132ace 100644
--- a/src/test/java/com/android/tools/r8/debug/LocalsTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.debug;
 
-import com.android.tools.r8.debug.DebugTestBase;
 import org.apache.harmony.jpda.tests.framework.jdwp.Value;
 import org.junit.Test;
 
@@ -12,6 +11,8 @@
  */
 public class LocalsTest extends DebugTestBase {
 
+  public static final String SOURCE_FILE = "Locals.java";
+
   @Test
   public void testNoLocal() throws Throwable {
     final String className = "Locals";
@@ -20,11 +21,11 @@
         breakpoint(className, methodName),
         run(),
         checkMethod(className, methodName),
-        checkLine(8),
+        checkLine(SOURCE_FILE, 8),
         checkNoLocal(),
         stepOver(),
         checkMethod(className, methodName),
-        checkLine(9),
+        checkLine(SOURCE_FILE, 9),
         checkNoLocal(),
         run());
   }
@@ -37,10 +38,10 @@
         breakpoint(className, methodName),
         run(),
         checkMethod(className, methodName),
-        checkLine(12),
+        checkLine(SOURCE_FILE, 12),
         checkNoLocal(),
         stepOver(),
-        checkLine(13),
+        checkLine(SOURCE_FILE, 13),
         checkLocal("i", Value.createInt(Integer.MAX_VALUE)),
         run());
   }
@@ -57,14 +58,14 @@
         breakpoint(className, methodName),
         run(),
         checkMethod(className, methodName),
-        checkLine(17),
+        checkLine(SOURCE_FILE, 17),
         checkLocal("p", pValue),
         stepOver(),
-        checkLine(18),
+        checkLine(SOURCE_FILE, 18),
         checkLocal("p", pValue),
         checkLocal("c", cValue),
         stepOver(),
-        checkLine(19),
+        checkLine(SOURCE_FILE, 19),
         checkLocal("p", pValue),
         checkLocal("c", cValue),
         checkLocal("v", vValue),
@@ -84,16 +85,16 @@
         breakpoint(className, methodName),
         run(),
         checkMethod(className, methodName),
-        checkLine(17),
+        checkLine(SOURCE_FILE, 17),
         checkLocal("p", pValue),
         stepOver(),
-        checkLine(18),
+        checkLine(SOURCE_FILE, 18),
         checkLocal("p", pValue),
         checkLocal("c", cValue),
         setLocal("c", newValue),
         checkLocal("c", newValue),  // we should see the updated value
         stepOver(),
-        checkLine(19),
+        checkLine(SOURCE_FILE, 19),
         checkLocal("p", pValue),
         checkLocal("c", newValue),
         checkLocal("v", vValue),
@@ -109,15 +110,15 @@
         breakpoint(className, methodName),
         run(),
         checkMethod(className, methodName),
-        checkLine(23),
+        checkLine(SOURCE_FILE, 23),
         checkNoLocal(),
         stepOver(),
         checkMethod(className, methodName),
-        checkLine(24),
+        checkLine(SOURCE_FILE, 24),
         checkLocal("i", Value.createInt(0)),
         setLocal("i", newValueForI),
         stepOver(),
-        checkLine(25),
+        checkLine(SOURCE_FILE, 25),
         checkLocal("i", newValueForI),
         checkLocal("f", Value.createFloat(0)),
         run());
@@ -133,15 +134,15 @@
         breakpoint(className, methodName),
         run(),
         checkMethod(className, methodName),
-        checkLine(29),
+        checkLine(SOURCE_FILE, 29),
         checkNoLocal(),
         stepOver(),
         checkMethod(className, methodName),
-        checkLine(30),
+        checkLine(SOURCE_FILE, 30),
         checkLocal("i", oldValueForI),
         setLocal("i", newValueForI),
         stepOver(),
-        checkLine(33),
+        checkLine(SOURCE_FILE, 33),
         checkLocal("i", newValueForI),
         run());
   }
diff --git a/src/test/java/com/android/tools/r8/debug/MultipleReturnsTest.java b/src/test/java/com/android/tools/r8/debug/MultipleReturnsTest.java
index 0b7973e..1d708cf 100644
--- a/src/test/java/com/android/tools/r8/debug/MultipleReturnsTest.java
+++ b/src/test/java/com/android/tools/r8/debug/MultipleReturnsTest.java
@@ -10,16 +10,18 @@
  */
 public class MultipleReturnsTest extends DebugTestBase {
 
+  public static final String SOURCE_FILE = "MultipleReturns.java";
+
   @Test
   public void testMultipleReturns() throws Throwable {
     runDebugTest("MultipleReturns",
         breakpoint("MultipleReturns", "multipleReturns"),
         run(),
         stepOver(),
-        checkLine(16), // this should be the 1st return statement
+        checkLine(SOURCE_FILE, 16), // this should be the 1st return statement
         run(),
         stepOver(),
-        checkLine(18), // this should be the 2nd return statement
+        checkLine(SOURCE_FILE, 18), // this should be the 2nd return statement
         run());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/debug/SyntheticMethodTest.java b/src/test/java/com/android/tools/r8/debug/SyntheticMethodTest.java
index a5b7d4e..fa5917e 100644
--- a/src/test/java/com/android/tools/r8/debug/SyntheticMethodTest.java
+++ b/src/test/java/com/android/tools/r8/debug/SyntheticMethodTest.java
@@ -11,22 +11,6 @@
 
 public class SyntheticMethodTest extends DebugTestBase {
 
-  private void debugInnerAccessors(StepFilter stepFilter) throws Throwable {
-    String debuggeeClass = "InnerAccessors";
-    List<Command> commands = new ArrayList<>();
-    commands.add(breakpoint("InnerAccessors$Inner", "callPrivateMethodInOuterClass"));
-    commands.add(run());
-    commands.add(checkLine(13));
-    commands.add(stepInto(stepFilter));  // skip synthetic accessor
-    if (stepFilter == NO_FILTER) {
-      commands.add(stepInto(stepFilter));
-    }
-    commands.add(checkMethod(debuggeeClass, "privateMethod"));
-    commands.add(checkLine(8));
-    commands.add(run());
-    runDebugTest(debuggeeClass, commands);
-  }
-
   @Test
   public void testInnerAccessors_NoFilter() throws Throwable {
     debugInnerAccessors(NO_FILTER);
@@ -37,4 +21,51 @@
     debugInnerAccessors(INTELLIJ_FILTER);
   }
 
+  @Test
+  public void testGenericBridges_NoFilter() throws Throwable {
+    debugGenericBridges(NO_FILTER);
+  }
+
+  @Test
+  public void testGenericBridges_IntelliJ() throws Throwable {
+    debugGenericBridges(INTELLIJ_FILTER);
+  }
+
+  private void debugInnerAccessors(StepFilter stepFilter) throws Throwable {
+    final String sourceFile = "InnerAccessors.java";
+    String debuggeeClass = "InnerAccessors";
+    List<Command> commands = new ArrayList<>();
+    commands.add(breakpoint("InnerAccessors$Inner", "callPrivateMethodInOuterClass"));
+    commands.add(run());
+    commands.add(checkLine(sourceFile, 13));
+    commands.add(stepInto(stepFilter));  // skip synthetic accessor
+    if (stepFilter == NO_FILTER) {
+      commands.add(stepInto(stepFilter));
+    }
+    commands.add(checkMethod(debuggeeClass, "privateMethod"));
+    commands.add(checkLine(sourceFile, 8));
+    commands.add(run());
+    runDebugTest(debuggeeClass, commands);
+  }
+
+  private void debugGenericBridges(StepFilter stepFilter) throws Throwable {
+    final String sourceFile = "Bridges.java";
+    String debuggeeClass = "Bridges";
+    List<Command> commands = new ArrayList<>();
+    commands.add(breakpoint(debuggeeClass, "testGenericBridge"));
+    commands.add(run());
+    commands.add(checkLine(sourceFile, 21));
+    commands.add(stepInto(stepFilter));  // skip synthetic accessor
+    String implementationClassName = "Bridges$StringImpl";
+    String methodName = "get";
+    if (stepFilter == NO_FILTER) {
+      commands.add(checkMethod(implementationClassName, methodName, "(Ljava/lang/Object;)V"));
+      commands.add(stepInto(stepFilter));
+    }
+    commands.add(checkMethod(implementationClassName, methodName, "(Ljava/lang/String;)V"));
+    commands.add(checkLine(sourceFile, 16));
+    commands.add(run());
+    runDebugTest(debuggeeClass, commands);
+  }
+
 }
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
index cab3291..ae0f650 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -4,41 +4,27 @@
 
 package com.android.tools.r8.maindexlist;
 
-import static com.android.tools.r8.ToolHelper.EXAMPLES_DIR;
 import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
 import static com.android.tools.r8.utils.FileUtils.ZIP_EXTENSION;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.CompilationResult;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.R8Command.Builder;
-import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.OffOrAuto;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 import java.util.concurrent.ExecutionException;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
 
 public class MainDexTracingTest {
@@ -155,11 +141,21 @@
           .map(dexType -> dexType.descriptor.toString())
           .collect(Collectors.toList());
       Collections.sort(resultMainDexList);
-      StringBuilder resultString = new StringBuilder();
-      resultMainDexList.forEach(descriptor -> resultString.append(descriptor).append('\n'));
-      String refList = new String(Files.readAllBytes(
-          expectedMainDexList), StandardCharsets.UTF_8);
-      Assert.assertEquals(refList, resultString.toString());
+      String[] refList = new String(Files.readAllBytes(
+          expectedMainDexList), StandardCharsets.UTF_8).split("\n");
+      for (int i = 0; i < refList.length; i++) {
+        String reference = refList[i];
+        String computed = resultMainDexList.get(i);
+        if (reference.contains("-$$Lambda$")) {
+          // For lambda classes we check that there is a lambda class for the right containing
+          // class. However, we do not check the hash for the generated lambda class. The hash
+          // changes for different compiler versions because different compiler versions generate
+          // different lambda implementation method names.
+          reference = reference.substring(0, reference.lastIndexOf('$'));
+          computed = computed.substring(0, computed.lastIndexOf('$'));
+        }
+        Assert.assertEquals(reference, computed);
+      }
     } catch (ExecutionException e) {
       throw e.getCause();
     }
diff --git a/tools/create_jctf_tests.py b/tools/create_jctf_tests.py
index 0f1ca98..52907fc 100755
--- a/tools/create_jctf_tests.py
+++ b/tools/create_jctf_tests.py
@@ -71,7 +71,8 @@
   raise IOError("Can't find package statement in java file: " + filepath)
 
 
-def generate_test(class_name, compiler_under_test, relative_package):
+def generate_test(class_name, compiler_under_test, compiler_under_test_enum,
+    relative_package):
   filename = join(DESTINATION_DIR, compiler_under_test,
       relative_package.replace('.', '/'), class_name + '.java')
   utils.makedirs_if_needed(dirname(filename))
@@ -83,7 +84,7 @@
       relativePackage = relative_package,
       name = full_class_name,
       testClassName = class_name,
-      compilerUnderTestEnum = compiler_under_test.upper(),
+      compilerUnderTestEnum = compiler_under_test_enum,
       classFile = full_class_name.replace('.', '/') + '.class',
       nameWithoutPackagePrefix = '{}.{}'.format(relative_package, class_name))
 
@@ -120,8 +121,8 @@
     assert idx >= 0
     relative_package = package[idx + len(dot_java_dot):]
 
-    for d in ['r8', 'd8']:
-      generate_test(class_name, d, relative_package)
+    generate_test(class_name, 'd8', 'R8DEBUG_AFTER_D8', relative_package)
+    generate_test(class_name, 'r8', 'R8', relative_package)
 
 
 if __name__ == '__main__':