Merge "Implement IR to CF conversion for the hello-world example program."
diff --git a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
index a6b45ae..219555e 100644
--- a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
+++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
@@ -35,15 +35,18 @@
     public final boolean forceProguardCompatibility;
     public final boolean ignoreMissingClasses;
     public final boolean multiDex;
+    public final String mainDexList;
     public final List<String> proguardConfig;
 
     CompatProguardOptions(List<String> proguardConfig, String output, int minApi,
-        boolean multiDex, boolean forceProguardCompatibility, boolean ignoreMissingClasses) {
+        boolean multiDex, boolean forceProguardCompatibility, boolean ignoreMissingClasses,
+        String mainDexList) {
       this.output = output;
       this.minApi = minApi;
       this.forceProguardCompatibility = forceProguardCompatibility;
       this.ignoreMissingClasses = ignoreMissingClasses;
       this.multiDex = multiDex;
+      this.mainDexList = mainDexList;
       this.proguardConfig = proguardConfig;
     }
 
@@ -53,12 +56,15 @@
       boolean forceProguardCompatibility = false;
       boolean ignoreMissingClasses = false;
       boolean multiDex = false;
+      String mainDexList = null;
+      // These two flags are currently ignored.
+      boolean minimalMainDex = false;
       boolean coreLibrary = false;
 
       ImmutableList.Builder<String> builder = ImmutableList.builder();
       if (args.length > 0) {
-        StringBuilder currentLine = new StringBuilder(args[0]);
-        for (int i = 1; i < args.length; i++) {
+        StringBuilder currentLine = new StringBuilder();
+        for (int i = 0; i < args.length; i++) {
           String arg = args[i];
           if (arg.charAt(0) == '-') {
             if (arg.equals("--min-api")) {
@@ -71,23 +77,34 @@
               output = args[++i];
             } else if (arg.equals("--multi-dex")) {
               multiDex = true;
+            } else if (arg.equals("--main-dex-list")) {
+              mainDexList = args[++i];
+            } else if (arg.startsWith("--main-dex-list=")) {
+              mainDexList = arg.substring("--main-dex-list=".length());
+            } else if (arg.equals("--minimal-main-dex")) {
+              minimalMainDex = true;
             } else if (arg.equals("--core-library")) {
               coreLibrary = true;
             } else if (arg.equals("-outjars")) {
               throw new CompilationException(
                   "Proguard argument -outjar is not supported. Use R8 compatible --output flag");
             } else {
-              builder.add(currentLine.toString());
+              if (currentLine.length() > 0) {
+                builder.add(currentLine.toString());
+              }
               currentLine = new StringBuilder(arg);
             }
           } else {
-            currentLine.append(' ').append(arg);
+            if (currentLine.length() > 0) {
+              currentLine.append(' ');
+            }
+            currentLine.append(arg);
           }
         }
         builder.add(currentLine.toString());
       }
       return new CompatProguardOptions(builder.build(), output, minApi, multiDex,
-          forceProguardCompatibility, ignoreMissingClasses);
+          forceProguardCompatibility, ignoreMissingClasses, mainDexList);
     }
   }
 
@@ -105,6 +122,9 @@
     builder.setOutputPath(Paths.get(options.output))
         .addProguardConfiguration(options.proguardConfig)
         .setMinApiLevel(options.minApi);
+    if (options.mainDexList != null) {
+      builder.addMainDexListFiles(Paths.get(options.mainDexList));
+    }
     AndroidApp result = R8.runInternal(builder.build());
 
     if (!options.multiDex) {
diff --git a/src/test/java/com/android/tools/r8/compatproguard/CompatProguardTest.java b/src/test/java/com/android/tools/r8/compatproguard/CompatProguardTest.java
new file mode 100644
index 0000000..638e700
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compatproguard/CompatProguardTest.java
@@ -0,0 +1,58 @@
+// 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.compatproguard;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.compatproguard.CompatProguard.CompatProguardOptions;
+import org.junit.Test;
+
+public class CompatProguardTest {
+
+  private CompatProguardOptions parseArgs(String... args)throws Exception  {
+    return CompatProguard.CompatProguardOptions.parse(args);
+  }
+
+  @Test
+  public void testProguardOptions() throws Exception {
+    CompatProguardOptions options;
+
+    options = parseArgs("-xxx");
+    assertEquals(1, options.proguardConfig.size());
+    options = parseArgs("-xxx", "xxx");
+    assertEquals(1, options.proguardConfig.size());
+    options = parseArgs("-xxx",  "-yyy");
+    assertEquals(2, options.proguardConfig.size());
+    options = parseArgs("-xxx", "xxx", "-yyy", "yyy");
+    assertEquals(2, options.proguardConfig.size());
+  }
+
+  @Test
+  public void testInjarsAndOutput() throws Exception {
+    CompatProguardOptions options;
+    String injars = "input.jar";
+    String output = "outputdir";
+    options = parseArgs("-injars", injars, "--output", output);
+    assertEquals(output, options.output);
+    assertEquals(1, options.proguardConfig.size());
+    options = parseArgs("--output", output, "-injars", injars);
+    assertEquals(1, options.proguardConfig.size());
+  }
+
+  @Test
+  public void testMainDexList() throws Exception {
+    CompatProguardOptions options;
+    String mainDexList = "maindexlist.txt";
+
+    options = parseArgs("--main-dex-list", mainDexList);
+    assertEquals(mainDexList, options.mainDexList);
+    options = parseArgs("--main-dex-list=" + mainDexList);
+    assertEquals(mainDexList, options.mainDexList);
+    options = parseArgs("--main-dex-list", mainDexList, "--minimal-main-dex");
+    assertEquals(mainDexList, options.mainDexList);
+    options = parseArgs("--minimal-main-dex", "--main-dex-list=" + mainDexList);
+    assertEquals(mainDexList, options.mainDexList);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java b/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java
index 3f5d788..44c9dd4 100644
--- a/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java
+++ b/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java
@@ -3,8 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.debug;
 
+import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
+import java.io.IOException;
 import org.junit.Assume;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -17,11 +19,15 @@
   public static final String CLASS = "BlockReordering";
   public static final String FILE = "BlockReordering.java";
 
+  private static DebuggeePath debuggeePath;
+
   @BeforeClass
-  public static void setUp() throws Exception {
+  public static void initDebuggeePath() throws IOException, CompilationException {
     // Force inversion of all conditionals to reliably construct a regression test for incorrect
     // line information when reordering blocks.
-    setUp(options -> options.testing.invertConditionals = true, null);
+    debuggeePath =
+        DebuggeePath.makeDex(
+            compileToDex(DEBUGGEE_JAR, options -> options.testing.invertConditionals = true));
   }
 
   @Test
@@ -30,14 +36,19 @@
         "Older runtimes incorrectly step out of function: b/67671565",
         ToolHelper.getDexVm().getVersion().isNewerThan(Version.V6_0_1));
     final String method = "conditionalReturn";
-    runDebugTest(CLASS,
+    runDebugTest(
+        debuggeePath,
+        CLASS,
         breakpoint(CLASS, method),
         run(),
-        checkLine(FILE, 8), stepOver(),
+        checkLine(FILE, 8),
+        stepOver(),
         checkLine(FILE, 13),
         run(),
-        checkLine(FILE, 8), stepOver(),
-        checkLine(FILE, 9), stepOver(),
+        checkLine(FILE, 8),
+        stepOver(),
+        checkLine(FILE, 9),
+        stepOver(),
         checkLine(FILE, 13),
         run());
   }
@@ -48,14 +59,19 @@
         "Older runtimes incorrectly step out of function: b/67671565",
         ToolHelper.getDexVm().getVersion().isNewerThan(Version.V6_0_1));
     final String method = "invertConditionalReturn";
-    runDebugTest(CLASS,
+    runDebugTest(
+        debuggeePath,
+        CLASS,
         breakpoint(CLASS, method),
         run(),
-        checkLine(FILE, 17), stepOver(),
-        checkLine(FILE, 18), stepOver(),
+        checkLine(FILE, 17),
+        stepOver(),
+        checkLine(FILE, 18),
+        stepOver(),
         checkLine(FILE, 22),
         run(),
-        checkLine(FILE, 17), stepOver(),
+        checkLine(FILE, 17),
+        stepOver(),
         checkLine(FILE, 22),
         run());
   }
@@ -66,18 +82,25 @@
         "Older runtimes incorrectly step out of function: b/67671565",
         ToolHelper.getDexVm().getVersion().isNewerThan(Version.V6_0_1));
     final String method = "fallthroughReturn";
-    runDebugTest(CLASS,
+    runDebugTest(
+        debuggeePath,
+        CLASS,
         breakpoint(CLASS, method),
         run(),
-        checkLine(FILE, 26), stepOver(),
+        checkLine(FILE, 26),
+        stepOver(),
         checkLine(FILE, 35),
         run(),
-        checkLine(FILE, 26), stepOver(),
-        checkLine(FILE, 30), stepOver(),
+        checkLine(FILE, 26),
+        stepOver(),
+        checkLine(FILE, 30),
+        stepOver(),
         checkLine(FILE, 35),
         run(),
-        checkLine(FILE, 26), stepOver(),
-        checkLine(FILE, 31), stepOver(),
+        checkLine(FILE, 26),
+        stepOver(),
+        checkLine(FILE, 31),
+        stepOver(),
         checkLine(FILE, 35),
         run());
   }
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 65e7dca..f6e680a 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -91,18 +91,33 @@
   public static final StepFilter INTELLIJ_FILTER = new StepFilter.IntelliJStepFilter();
   private static final StepFilter DEFAULT_FILTER = NO_FILTER;
 
-  enum RuntimeKind {
-    JAVA,
-    ART
-  }
-
   enum DexCompilerKind {
     DX,
     D8
   }
 
-  // Set to JAVA to run tests with java
-  private static final RuntimeKind RUNTIME_KIND = RuntimeKind.ART;
+  enum BinaryKind {
+    CF,
+    DEX
+  }
+
+  protected static class DebuggeePath {
+    public final BinaryKind kind;
+    public final Path path;
+
+    public static DebuggeePath makeDex(Path path) {
+      return new DebuggeePath(BinaryKind.DEX, path);
+    }
+
+    public static DebuggeePath makeClassFile(Path path) {
+      return new DebuggeePath(BinaryKind.CF, path);
+    }
+
+    public DebuggeePath(BinaryKind kind, Path path) {
+      this.kind = kind;
+      this.path = path;
+    }
+  }
 
   private static final DexCompilerKind DEX_COMPILER_KIND = DexCompilerKind.D8;
 
@@ -111,8 +126,8 @@
 
   private static final Path JDWP_JAR = ToolHelper
       .getJdwpTestsJarPath(ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm()));
-  private static final Path DEBUGGEE_JAR = Paths
-      .get(ToolHelper.BUILD_DIR, "test", "debug_test_resources.jar");
+  protected static final Path DEBUGGEE_JAR =
+      Paths.get(ToolHelper.BUILD_DIR, "test", "debug_test_resources.jar");
   private static final Path DEBUGGEE_JAVA8_JAR = Paths
       .get(ToolHelper.BUILD_DIR, "test", "debug_test_resources_java8.jar");
   private static final Path DEBUGGEE_KOTLIN_JAR = Paths
@@ -121,42 +136,63 @@
 
   @ClassRule
   public static TemporaryFolder temp = new TemporaryFolder();
-  private static Path jdwpDexD8 = null;
-  private static Path debuggeeDexD8 = null;
-  private static Path debuggeeDexR8 = null;
-  private static Path debuggeeJava8DexD8 = null;
-  private static Path debuggeeKotlinDexD8 = null;
+
+  // TODO(tamaskenez): Separate test setup from test runner.
+  private static Path jdwpDexD8;
+  private static Path debuggeeDexD8;
+  private static Path debuggeeJava8DexD8;
+  private static Path debuggeeKotlinDexD8;
 
   @Rule
   public TestName testName = new TestName();
 
   @BeforeClass
   public static void setUp() throws Exception {
-    setUp(null, null);
+    jdwpDexD8 = compileToDex(JDWP_JAR, null);
+    debuggeeDexD8 = null;
+    debuggeeJava8DexD8 = null;
+    debuggeeKotlinDexD8 = null;
   }
 
-  protected static List<String> proguardConfigurations = Collections.emptyList();
-  protected static boolean writeProguardMap = false;
+  protected static synchronized Path getDebuggeeDexD8() throws IOException, CompilationException {
+    if (debuggeeDexD8 == null) {
+      debuggeeDexD8 = compileToDex(DEBUGGEE_JAR, null);
+    }
+    return debuggeeDexD8;
+  }
 
-  protected static void setUp(
-      Consumer<InternalOptions> optionsConsumer,
-      Consumer<ProguardConfiguration.Builder> pgConsumer)
-      throws Exception {
-    // TODO(zerny): supply a set of compilers to run with.
-    // Compile the debuggee code first so potential compilation errors or debugging breakpoints hit
-    // here first.
-    debuggeeDexD8 = compileToDex(DEBUGGEE_JAR, optionsConsumer);
-    debuggeeDexR8 = compileToDexViaR8(optionsConsumer, pgConsumer, DEBUGGEE_JAR);
-    debuggeeJava8DexD8 = compileToDex(DEBUGGEE_JAVA8_JAR, options -> {
-      // Enable desugaring for preN runtimes
-      options.interfaceMethodDesugaring = OffOrAuto.Auto;
-      if (optionsConsumer != null) {
-        optionsConsumer.accept(options);
-      }
-    });
-    debuggeeKotlinDexD8 = compileToDex(DEBUGGEE_KOTLIN_JAR, optionsConsumer);
-    // Convert jar to dex with d8 with debug info
-    jdwpDexD8 = compileToDex(JDWP_JAR, null);
+  private static synchronized Path getDebuggeeJava8DexD8()
+      throws IOException, CompilationException {
+    if (debuggeeJava8DexD8 == null) {
+      debuggeeJava8DexD8 =
+          compileToDex(
+              DEBUGGEE_JAVA8_JAR,
+              options -> {
+                // Enable desugaring for preN runtimes
+                options.interfaceMethodDesugaring = OffOrAuto.Auto;
+              });
+    }
+    return debuggeeJava8DexD8;
+  }
+
+  private static synchronized Path getDebuggeeKotlinDexD8()
+      throws IOException, CompilationException {
+    if (debuggeeKotlinDexD8 == null) {
+      debuggeeKotlinDexD8 = compileToDex(DEBUGGEE_KOTLIN_JAR, null);
+    }
+    return debuggeeKotlinDexD8;
+  }
+
+  protected static DebuggeePath getDebuggeeDexD8OrCf(boolean cf)
+      throws IOException, CompilationException {
+    return cf ? DebuggeePath.makeClassFile(DEBUGGEE_JAR) : DebuggeePath.makeDex(getDebuggeeDexD8());
+  }
+
+  protected static DebuggeePath getDebuggeeJava8DexD8OrCf(boolean cf)
+      throws IOException, CompilationException {
+    return cf
+        ? DebuggeePath.makeClassFile(DEBUGGEE_JAVA8_JAR)
+        : DebuggeePath.makeDex(getDebuggeeJava8DexD8());
   }
 
   protected static Path compileToDex(Path jarToCompile, Consumer<InternalOptions> optionsConsumer)
@@ -202,10 +238,12 @@
     return dexOutputDir.resolve("classes.dex");
   }
 
-  static Path compileToDexViaR8(
+  public static Path compileToDexViaR8(
       Consumer<InternalOptions> optionsConsumer,
       Consumer<ProguardConfiguration.Builder> pgConsumer,
-      Path jarToCompile)
+      Path jarToCompile,
+      List<String> proguardConfigurations,
+      boolean writeProguardMap)
       throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
     int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
     assert jarToCompile.toFile().exists();
@@ -230,110 +268,110 @@
     return dexOutputDir.resolve("classes.dex");
   }
 
-  protected final boolean supportsDefaultMethod() {
-    return RUNTIME_KIND == RuntimeKind.JAVA ||
-        ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm()) >= AndroidApiLevel.N.getLevel();
+  private BinaryKind currentlyRunningBinaryKind = null;
+
+  protected final BinaryKind getCurrentlyRunningBinaryKind() {
+    if (currentlyRunningBinaryKind == null) {
+      throw new RuntimeException("Nothing is running currently.");
+    }
+    return currentlyRunningBinaryKind;
   }
 
-  protected static boolean isRunningJava() {
-    return RUNTIME_KIND == RuntimeKind.JAVA;
+  protected static final boolean supportsDefaultMethod(boolean isRunningJava) {
+    return isRunningJava
+        || ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm()) >= AndroidApiLevel.N.getLevel();
   }
 
-  protected static boolean isRunningArt() {
-    return RUNTIME_KIND == RuntimeKind.ART;
+  protected final boolean isRunningJava() {
+    return getCurrentlyRunningBinaryKind() == BinaryKind.CF;
+  }
+
+  protected final boolean isRunningArt() {
+    return getCurrentlyRunningBinaryKind() == BinaryKind.DEX;
   }
 
   protected final void runDebugTest(String debuggeeClass, JUnit3Wrapper.Command... commands)
       throws Throwable {
-    runDebugTest(Collections.emptyList(), debuggeeClass, Arrays.asList(commands));
+    runDebugTest(
+        DebuggeePath.makeDex(getDebuggeeDexD8()),
+        Collections.<Path>emptyList(),
+        debuggeeClass,
+        Arrays.asList(commands));
   }
 
   protected final void runDebugTest(List<Path> extraPaths, String debuggeeClass,
       JUnit3Wrapper.Command... commands) throws Throwable {
-    runDebugTest(extraPaths, debuggeeClass, Arrays.asList(commands));
+    runDebugTest(
+        DebuggeePath.makeDex(getDebuggeeDexD8()),
+        extraPaths,
+        debuggeeClass,
+        Arrays.asList(commands));
   }
 
   protected final void runDebugTest(String debuggeeClass, List<JUnit3Wrapper.Command> commands)
       throws Throwable {
-    runDebugTest(Collections.emptyList(), LanguageFeatures.JAVA_7, debuggeeClass, commands);
-  }
-
-  protected final void runDebugTest(List<Path> extraPaths, String debuggeeClass,
-      List<JUnit3Wrapper.Command> commands) throws Throwable {
-    runDebugTest(extraPaths, LanguageFeatures.JAVA_7, debuggeeClass, commands);
+    runDebugTest(
+        DebuggeePath.makeDex(getDebuggeeDexD8()),
+        Collections.<Path>emptyList(),
+        debuggeeClass,
+        commands);
   }
 
   protected final void runDebugTestJava8(String debuggeeClass, JUnit3Wrapper.Command... commands)
       throws Throwable {
-    runDebugTestJava8(debuggeeClass, Arrays.asList(commands));
+    runDebugTest(
+        DebuggeePath.makeDex(getDebuggeeJava8DexD8()),
+        Collections.<Path>emptyList(),
+        debuggeeClass,
+        Arrays.asList(commands));
   }
 
   protected final void runDebugTestJava8(String debuggeeClass, List<JUnit3Wrapper.Command> commands)
       throws Throwable {
-    runDebugTest(Collections.emptyList(), LanguageFeatures.JAVA_8, debuggeeClass, commands);
+    runDebugTest(
+        DebuggeePath.makeDex(getDebuggeeJava8DexD8()),
+        Collections.<Path>emptyList(),
+        debuggeeClass,
+        commands);
   }
 
   protected final void runDebugTestKotlin(String debuggeeClass, JUnit3Wrapper.Command... commands)
       throws Throwable {
-    runDebugTestKotlin(debuggeeClass, Arrays.asList(commands));
+    runDebugTest(
+        DebuggeePath.makeDex(getDebuggeeKotlinDexD8()),
+        Collections.<Path>emptyList(),
+        debuggeeClass,
+        Arrays.asList(commands));
   }
 
-  protected final void runDebugTestKotlin(String debuggeeClass,
-      List<JUnit3Wrapper.Command> commands) throws Throwable {
-    runDebugTest(Collections.emptyList(), LanguageFeatures.KOTLIN, debuggeeClass, commands);
-  }
-
-  protected final void runDebugTestR8(String debuggeeClass, JUnit3Wrapper.Command... commands)
+  protected void runDebugTest(
+      DebuggeePath debuggeePath, String debuggeeClass, JUnit3Wrapper.Command... commands)
       throws Throwable {
-    runDebugTestR8(debuggeeClass, Arrays.asList(commands));
+    runDebugTest(
+        debuggeePath, Collections.<Path>emptyList(), debuggeeClass, Arrays.asList(commands));
   }
 
-  protected final void runDebugTestR8(String debuggeeClass, List<JUnit3Wrapper.Command> commands)
+  protected void runDebugTest(
+      DebuggeePath debuggeePath, String debuggeeClass, List<JUnit3Wrapper.Command> commands)
       throws Throwable {
-    runDebugTest(Collections.emptyList(), LanguageFeatures.R8, debuggeeClass, commands);
+    runDebugTest(debuggeePath, Collections.<Path>emptyList(), debuggeeClass, commands);
   }
 
-  protected enum LanguageFeatures {
-    JAVA_7(DEBUGGEE_JAR) {
-      @Override
-      public Path getDexPath() {
-        return debuggeeDexD8;
-      }
-    },
-    JAVA_8(DEBUGGEE_JAVA8_JAR) {
-      @Override
-      public Path getDexPath() {
-        return debuggeeJava8DexD8;
-      }
-    },
-    KOTLIN(DEBUGGEE_KOTLIN_JAR) {
-      @Override
-      public Path getDexPath() {
-        return debuggeeKotlinDexD8;
-      }
-    },
-    R8(DEBUGGEE_JAR) {
-      @Override
-      public Path getDexPath() {
-        return debuggeeDexR8;
-      }
-    };
-
-    private final Path jarPath;
-
-    LanguageFeatures(Path jarPath) {
-      this.jarPath = jarPath;
-    }
-
-    public Path getJarPath() {
-      return jarPath;
-    }
-
-    public abstract Path getDexPath();
+  protected void runDebugTest(
+      DebuggeePath debuggeePath,
+      List<Path> extraPaths,
+      String debuggeeClass,
+      JUnit3Wrapper.Command... commands)
+      throws Throwable {
+    runDebugTest(debuggeePath, extraPaths, debuggeeClass, Arrays.asList(commands));
   }
 
-  private void runDebugTest(List<Path> extraPaths, LanguageFeatures languageFeatures,
-      String debuggeeClass, List<JUnit3Wrapper.Command> commands) throws Throwable {
+  protected void runDebugTest(
+      DebuggeePath debuggeePath,
+      List<Path> extraPaths,
+      String debuggeeClass,
+      List<JUnit3Wrapper.Command> commands)
+      throws Throwable {
     // Skip test due to unsupported runtime.
     Assume.assumeTrue("Skipping test " + testName.getMethodName() + " because ART is not supported",
         ToolHelper.artSupported());
@@ -344,22 +382,22 @@
     String[] paths = new String[extraPaths.size() + 2];
     int indexPath = 0;
     ClassNameMapper classNameMapper = null;
-    if (RUNTIME_KIND == RuntimeKind.JAVA) {
+    if (debuggeePath.kind == BinaryKind.CF) {
       paths[indexPath++] = JDWP_JAR.toString();
-      paths[indexPath++] = languageFeatures.getJarPath().toString();
     } else {
       paths[indexPath++] = jdwpDexD8.toString();
-      paths[indexPath++] = languageFeatures.getDexPath().toString();
-      Path proguardMapPath = Paths.get(paths[indexPath - 1]).resolveSibling(PROGUARD_MAP_FILENAME);
+      Path proguardMapPath = debuggeePath.path.resolveSibling(PROGUARD_MAP_FILENAME);
       if (Files.exists(proguardMapPath)) {
         classNameMapper = ClassNameMapper.mapperFromFile(proguardMapPath);
       }
     }
+    paths[indexPath++] = debuggeePath.path.toString();
     for (Path extraPath : extraPaths) {
       paths[indexPath++] = extraPath.toString();
     }
 
-    new JUnit3Wrapper(debuggeeClass, paths, commands, classNameMapper).runBare();
+    currentlyRunningBinaryKind = debuggeePath.kind;
+    new JUnit3Wrapper(debuggeeClass, paths, commands, classNameMapper, isRunningArt()).runBare();
   }
 
   protected final JUnit3Wrapper.Command run() {
@@ -566,6 +604,8 @@
     // Active event requests.
     private final Map<Integer, EventHandler> events = new TreeMap<>();
 
+    private final boolean isRunningArt;
+
     /**
      * The Translator interface provides mapping between the class and method names and line numbers
      * found in the binary file and their original forms.
@@ -719,7 +759,8 @@
         String debuggeeClassName,
         String[] debuggeePath,
         List<Command> commands,
-        ClassNameMapper classNameMapper) {
+        ClassNameMapper classNameMapper,
+        boolean isRunningArt) {
       this.debuggeeClassName = debuggeeClassName;
       this.debuggeePath = debuggeePath;
       this.commandsQueue = new ArrayDeque<>(commands);
@@ -728,6 +769,7 @@
       } else {
         this.translator = new ClassNameMapperTranslator(classNameMapper);
       }
+      this.isRunningArt = isRunningArt;
     }
 
     @Override
@@ -864,7 +906,7 @@
 
         ArtTestOptions(String[] debuggeePath) {
           // Set debuggee command-line.
-          if (RUNTIME_KIND == RuntimeKind.ART) {
+          if (isRunningArt) {
             ArtCommandBuilder artCommandBuilder = new ArtCommandBuilder(ToolHelper.getDexVm());
             if (ToolHelper.getDexVm().getVersion().isNewerThan(DexVm.Version.V5_1_1)) {
               artCommandBuilder.appendArtOption("-Xcompiler-option");
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 91fed63..666770c 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestExamples.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestExamples.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.debug;
 
-import java.util.Collections;
 import org.apache.harmony.jpda.tests.framework.jdwp.Value;
 import org.junit.Test;
 
@@ -20,7 +19,7 @@
    */
   @Test
   public void testRun() throws Throwable {
-    runDebugTest(DEBUGGEE_CLASS, Collections.singletonList(run()));
+    runDebugTest(DEBUGGEE_CLASS, run());
   }
 
   /**
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 7ca9eae..231a8c9 100644
--- a/src/test/java/com/android/tools/r8/debug/ExceptionTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ExceptionTest.java
@@ -11,12 +11,15 @@
 public class ExceptionTest extends DebugTestBase {
 
   public static final String SOURCE_FILE = "Exceptions.java";
+  public static final boolean RUN_JAVA = false;
 
   @Test
   public void testStepOnCatch() throws Throwable {
-    if (isRunningJava()) {
+    if (RUN_JAVA) {
       // Java jumps to first instruction of the catch handler, matching the source code.
-      runDebugTest("Exceptions",
+      runDebugTest(
+          DebuggeePath.makeClassFile(DEBUGGEE_JAR),
+          "Exceptions",
           breakpoint("Exceptions", "catchException"),
           run(),
           checkLine(SOURCE_FILE, 9), // line of the method call throwing the exception
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 713d042..95edf7c 100644
--- a/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
@@ -4,21 +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.ToolHelper.DexVm.Version;
 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 {
 
   private static final String SOURCE_FILE = "DebugInterfaceMethod.java";
+  private static final boolean RUN_JAVA = false;
 
   @Test
   public void testDefaultMethod() throws Throwable {
@@ -31,7 +25,7 @@
     commands.add(run());
     commands.add(checkMethod(debuggeeClass, "testDefaultMethod"));
     commands.add(checkLine(SOURCE_FILE, 31));
-    if (!supportsDefaultMethod()) {
+    if (!supportsDefaultMethod(RUN_JAVA)) {
       // We desugared default method. This means we're going to step through an extra (forward)
       // method first.
       commands.add(stepInto(INTELLIJ_FILTER));
@@ -39,7 +33,7 @@
     commands.add(stepInto(INTELLIJ_FILTER));
     commands.add(checkLine(SOURCE_FILE, 9));
     // TODO(shertz) we should see the local variable this even when desugaring.
-    if (supportsDefaultMethod()) {
+    if (supportsDefaultMethod(RUN_JAVA)) {
       commands.add(checkLocal("this"));
     }
     commands.add(checkLocal(parameterName));
@@ -50,7 +44,7 @@
     commands.add(run());
     commands.add(run()  /* resume after 2nd breakpoint */);
 
-    runDebugTestJava8(debuggeeClass, commands);
+    runDebugTest(getDebuggeeJava8DexD8OrCf(RUN_JAVA), debuggeeClass, commands);
   }
 
   @Test
@@ -75,7 +69,7 @@
     commands.add(checkLocal(localVariableName));
     commands.add(run());
 
-    runDebugTestJava8(debuggeeClass, commands);
+    runDebugTest(getDebuggeeJava8DexD8OrCf(RUN_JAVA), debuggeeClass, commands);
   }
 
   @Test
@@ -94,6 +88,6 @@
     commands.add(checkLocal(parameterName));
     commands.add(run());
 
-    runDebugTestJava8(debuggeeClass, commands);
+    runDebugTest(getDebuggeeJava8DexD8OrCf(RUN_JAVA), debuggeeClass, commands);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java b/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java
index ddcbf8c..6f9c726 100644
--- a/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java
+++ b/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java
@@ -22,6 +22,8 @@
 // TODO(b/65474850) Should we build Jasmin at compile time or runtime ?
 public class JasminDebugTest extends DebugTestBase {
 
+  public static final boolean RUN_JAVA = false;
+
   @Rule
   public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
@@ -31,7 +33,9 @@
     final String sourcefile = className + ".j";
     final String methodName = "test";
     List<Path> paths = getExtraPaths(getBuilderForUselessCheckcast(className, methodName));
-    runDebugTest(paths,
+    runDebugTest(
+        getDebuggeeDexD8OrCf(RUN_JAVA),
+        paths,
         className,
         breakpoint(className, methodName),
         run(),
@@ -91,7 +95,7 @@
       try (OutputStream outputStream = Files.newOutputStream(path)) {
         file.write(outputStream);
       }
-      if (isRunningJava()) {
+      if (RUN_JAVA) {
         extraPaths.add(path);
       } else {
         extraPaths.add(compileToDex(path, null));
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 eba125c..a9009b6 100644
--- a/src/test/java/com/android/tools/r8/debug/LambdaTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LambdaTest.java
@@ -11,12 +11,15 @@
 public class LambdaTest extends DebugTestBase {
 
   public static final String SOURCE_FILE = "DebugLambda.java";
+  private static final boolean RUN_JAVA = false;
 
   @Test
   public void testLambda_ExpressionOnSameLine() throws Throwable {
     String debuggeeClass = "DebugLambda";
     String initialMethodName = "printInt";
-    runDebugTestJava8(debuggeeClass,
+    runDebugTest(
+        getDebuggeeJava8DexD8OrCf(RUN_JAVA),
+        debuggeeClass,
         breakpoint(debuggeeClass, initialMethodName),
         run(),
         checkMethod(debuggeeClass, initialMethodName),
@@ -30,7 +33,9 @@
   public void testLambda_StatementOnNewLine() throws Throwable {
     String debuggeeClass = "DebugLambda";
     String initialMethodName = "printInt3";
-    runDebugTestJava8(debuggeeClass,
+    runDebugTest(
+        getDebuggeeJava8DexD8OrCf(RUN_JAVA),
+        debuggeeClass,
         breakpoint(debuggeeClass, initialMethodName),
         run(),
         checkMethod(debuggeeClass, initialMethodName),
@@ -44,13 +49,15 @@
   public void testLambda_StaticMethodReference_Trivial() throws Throwable {
     String debuggeeClass = "DebugLambda";
     String initialMethodName = "printInt2";
-    runDebugTestJava8(debuggeeClass,
+    runDebugTest(
+        getDebuggeeJava8DexD8OrCf(RUN_JAVA),
+        debuggeeClass,
         breakpoint(debuggeeClass, initialMethodName),
         run(),
         checkMethod(debuggeeClass, initialMethodName),
         checkLine(SOURCE_FILE, 20),
         stepInto(INTELLIJ_FILTER),
-        isRunningJava() ? LambdaTest::doNothing : stepInto(INTELLIJ_FILTER),
+        RUN_JAVA ? LambdaTest::doNothing : stepInto(INTELLIJ_FILTER),
         checkMethod(debuggeeClass, "returnOne"),
         checkLine(SOURCE_FILE, 28),
         checkNoLocal(),
@@ -61,7 +68,9 @@
   public void testLambda_StaticMethodReference_NonTrivial() throws Throwable {
     String debuggeeClass = "DebugLambda";
     String initialMethodName = "testLambdaWithMethodReferenceAndConversion";
-    runDebugTestJava8(debuggeeClass,
+    runDebugTest(
+        getDebuggeeJava8DexD8OrCf(RUN_JAVA),
+        debuggeeClass,
         breakpoint(debuggeeClass, initialMethodName),
         run(),
         checkMethod(debuggeeClass, initialMethodName),
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 c86581f..1ef7f3e 100644
--- a/src/test/java/com/android/tools/r8/debug/LocalsTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
@@ -9,7 +9,6 @@
 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.Tag;
 import org.apache.harmony.jpda.tests.framework.jdwp.Value;
 import org.junit.Assert;
-import org.junit.Ignore;
 import org.junit.Test;
 
 /**
diff --git a/src/test/java/com/android/tools/r8/debug/MinificationTest.java b/src/test/java/com/android/tools/r8/debug/MinificationTest.java
index f4d566c..9c0702f 100644
--- a/src/test/java/com/android/tools/r8/debug/MinificationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/MinificationTest.java
@@ -3,10 +3,17 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.debug;
 
+import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.TestBase.MinifyMode;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
 import java.util.Collection;
-import org.junit.BeforeClass;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -16,6 +23,21 @@
 public class MinificationTest extends DebugTestBase {
 
   public static final String SOURCE_FILE = "Minified.java";
+  private static final HashMap<Config, Path> debuggeePathMap = new HashMap<>();
+
+  private static class Config {
+    public final MinifyMode minificationMode;
+    public final boolean writeProguardMap;
+
+    Config(MinifyMode minificationMode, boolean writeProguardMap) {
+      this.minificationMode = minificationMode;
+      this.writeProguardMap = writeProguardMap;
+    }
+
+    public boolean minifiedNames() {
+      return minificationMode.isMinify() && !writeProguardMap;
+    }
+  }
 
   @Parameterized.Parameters(name = "minification: {0}, proguardMap: {1}")
   public static Collection minificationControl() {
@@ -29,47 +51,46 @@
     return builder.build();
   }
 
-  private static boolean firstRun = true;
-  private static MinifyMode minificationMode;
+  private final Config config;
 
-  public MinificationTest(MinifyMode minificationMode, boolean writeProguardMap) throws Exception {
-    // TODO(tamaskenez) The way we're shadowing and calling the static setUp() methods should be
-    // updated when we refactor DebugTestBase.
-    if (firstRun
-        || MinificationTest.minificationMode != minificationMode
-        || DebugTestBase.writeProguardMap != writeProguardMap) {
-
-      firstRun = false;
-      MinificationTest.minificationMode = minificationMode;
-      DebugTestBase.writeProguardMap = writeProguardMap;
-
-      if (MinificationTest.minificationMode.isMinify()) {
+  private synchronized DebuggeePath getDebuggeePath()
+      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+    Path path = debuggeePathMap.get(config);
+    if (path == null) {
+      List<String> proguardConfigurations = Collections.<String>emptyList();
+      if (config.minificationMode.isMinify()) {
         ImmutableList.Builder<String> builder = ImmutableList.builder();
         builder.add("-keep public class Minified { public static void main(java.lang.String[]); }");
         builder.add("-keepattributes SourceFile");
         builder.add("-keepattributes LineNumberTable");
-        if (minificationMode == MinifyMode.AGGRESSIVE) {
+        if (config.minificationMode == MinifyMode.AGGRESSIVE) {
           builder.add("-overloadaggressively");
         }
         proguardConfigurations = builder.build();
       }
-      setUp(null, null);
+      path =
+          compileToDexViaR8(
+              null, null, DEBUGGEE_JAR, proguardConfigurations, config.writeProguardMap);
+      debuggeePathMap.put(config, path);
     }
+    return DebuggeePath.makeDex(path);
   }
 
-  @BeforeClass
-  public static void setUp() throws Exception {}
+  public MinificationTest(MinifyMode minificationMode, boolean writeProguardMap) throws Exception {
+    config = new Config(minificationMode, writeProguardMap);
+  }
 
   @Test
   public void testBreakInMainClass() throws Throwable {
-    boolean minifiedNames = (minificationMode.isMinify() && !writeProguardMap);
     final String className = "Minified";
-    final String methodName = minifiedNames ? "a" : "test";
+    final String methodName = config.minifiedNames() ? "a" : "test";
     final String signature = "()V";
-    final String innerClassName = minifiedNames ? "a" : "Minified$Inner";
-    final String innerMethodName = minifiedNames ? "a" : "innerTest";
+    final String innerClassName = config.minifiedNames() ? "a" : "Minified$Inner";
+    final String innerMethodName = config.minifiedNames() ? "a" : "innerTest";
     final String innerSignature = "()I";
-    runDebugTestR8(className,
+    runDebugTest(
+        getDebuggeePath(),
+        className,
         breakpoint(className, methodName, signature),
         run(),
         checkMethod(className, methodName, signature),
@@ -85,12 +106,12 @@
 
   @Test
   public void testBreakInPossiblyRenamedClass() throws Throwable {
-    boolean minifiedNames = (minificationMode.isMinify() && !writeProguardMap);
     final String className = "Minified";
-    final String innerClassName = minifiedNames ? "a" : "Minified$Inner";
-    final String innerMethodName = minifiedNames ? "a" : "innerTest";
+    final String innerClassName = config.minifiedNames() ? "a" : "Minified$Inner";
+    final String innerMethodName = config.minifiedNames() ? "a" : "innerTest";
     final String innerSignature = "()I";
-    runDebugTestR8(
+    runDebugTest(
+        getDebuggeePath(),
         className,
         breakpoint(innerClassName, innerMethodName, innerSignature),
         run(),
diff --git a/src/test/java/com/android/tools/r8/debug/SmaliDebugTest.java b/src/test/java/com/android/tools/r8/debug/SmaliDebugTest.java
index 9c5a636..66652bd 100644
--- a/src/test/java/com/android/tools/r8/debug/SmaliDebugTest.java
+++ b/src/test/java/com/android/tools/r8/debug/SmaliDebugTest.java
@@ -30,11 +30,6 @@
   static final String FILE = "SmaliDebugTestDebuggee.smali";
   static final String CLASS = "SmaliDebugTestDebuggee";
 
-  @BeforeClass
-  public static void beforeClass() throws Exception {
-    Assume.assumeTrue(DebugTestBase.isRunningArt());
-  }
-
   @Rule
   public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
diff --git a/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java b/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java
index 427c253..46991d6 100644
--- a/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java
+++ b/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java
@@ -3,9 +3,14 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
+import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.debug.DebugTestBase;
 import com.android.tools.r8.shaking.ProguardKeepRule;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.concurrent.ExecutionException;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
@@ -16,16 +21,24 @@
 
   private static final String TEST_FILE = "TestFile.java";
 
+  private static DebuggeePath debuggeePath;
+
   @BeforeClass
-  public static void setUp() throws Exception {
-    setUp(
-        null,
-        pg -> {
-          pg.resetProguardDefaults();
-          pg.addRule(ProguardKeepRule.defaultKeepAllRule());
-          pg.setRenameSourceFileAttribute(TEST_FILE);
-          pg.addKeepAttributePatterns(ImmutableList.of("SourceFile", "LineNumberTable"));
-        });
+  public static void initDebuggeePath()
+      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+    debuggeePath =
+        DebuggeePath.makeDex(
+            compileToDexViaR8(
+                null,
+                pg -> {
+                  pg.resetProguardDefaults();
+                  pg.addRule(ProguardKeepRule.defaultKeepAllRule());
+                  pg.setRenameSourceFileAttribute(TEST_FILE);
+                  pg.addKeepAttributePatterns(ImmutableList.of("SourceFile", "LineNumberTable"));
+                },
+                DEBUGGEE_JAR,
+                Collections.<String>emptyList(),
+                false));
   }
 
   /**
@@ -34,11 +47,8 @@
   @Test
   public void testBreakpointInEmptyClassInitializer() throws Throwable {
     final String CLASS = "ClassInitializerEmpty";
-    runDebugTestR8(CLASS,
-        breakpoint(CLASS, "<clinit>"),
-        run(),
-        checkLine(TEST_FILE, 8),
-        run());
+    runDebugTest(
+        debuggeePath, CLASS, breakpoint(CLASS, "<clinit>"), run(), checkLine(TEST_FILE, 8), run());
   }
 
   /**
@@ -49,7 +59,9 @@
   public void testNoLocal() throws Throwable {
     final String className = "Locals";
     final String methodName = "noLocals";
-    runDebugTestR8(className,
+    runDebugTest(
+        debuggeePath,
+        className,
         breakpoint(className, methodName),
         run(),
         checkMethod(className, methodName),
@@ -67,7 +79,9 @@
    */
   @Test
   public void testMultipleReturns() throws Throwable {
-    runDebugTestR8("MultipleReturns",
+    runDebugTest(
+        debuggeePath,
+        "MultipleReturns",
         breakpoint("MultipleReturns", "multipleReturns"),
         run(),
         stepOver(),