Merge "Remove DebugLocalWrite when it is possible"
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index e8990f3..1306e3b 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -63,7 +63,9 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -452,21 +454,31 @@
     }
   }
 
+  private static void writeProguardMapToPath(Path path, AndroidApp outputApp) throws IOException {
+    try (Closer closer = Closer.create()) {
+      OutputStream mapOut = FileUtils.openPathWithDefault(
+          closer,
+          path,
+          System.out,
+          StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
+      outputApp.writeProguardMap(mapOut);
+    }
+  }
+
   static void writeOutputs(R8Command command, InternalOptions options, AndroidApp outputApp)
       throws IOException {
     if (command.getOutputPath() != null) {
       outputApp.write(command.getOutputPath(), options.outputMode);
     }
 
-    if (options.proguardConfiguration.isPrintMapping() && !options.skipMinification) {
+    if ((options.proguardConfiguration.isPrintMapping() || options.printMappingFile != null)
+        && !options.skipMinification) {
       assert outputApp.hasProguardMap();
-      try (Closer closer = Closer.create()) {
-        OutputStream mapOut = FileUtils.openPathWithDefault(
-            closer,
-            options.proguardConfiguration.getPrintMappingFile(),
-            System.out,
-            StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
-        outputApp.writeProguardMap(mapOut);
+      if (options.proguardConfiguration.isPrintMapping()) {
+        writeProguardMapToPath(options.proguardConfiguration.getPrintMappingFile(), outputApp);
+      }
+      if (options.printMappingFile != null) {
+        writeProguardMapToPath(options.printMappingFile, outputApp);
       }
     }
     if (options.proguardConfiguration.isPrintSeeds()) {
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index b860de4..301c266 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -36,6 +36,7 @@
     private Optional<Boolean> discardedChecker = Optional.empty();
     private Optional<Boolean> minification = Optional.empty();
     private boolean ignoreMissingClasses = false;
+    private Path printMappingFile = null;
     private Path packageDistributionFile = null;
 
     private Builder() {
@@ -176,6 +177,11 @@
       return self();
     }
 
+    Builder setPrintMappingFile(Path path) {
+      this.printMappingFile = path;
+      return self();
+    }
+
     protected void validate() throws CompilationException {
       super.validate();
       if (mainDexListOutput != null && mainDexRules.isEmpty() && !getAppBuilder()
@@ -251,7 +257,8 @@
           useTreeShaking,
           useDiscardedChecker,
           useMinification,
-          ignoreMissingClasses);
+          ignoreMissingClasses,
+          printMappingFile);
     }
   }
 
@@ -274,6 +281,7 @@
       "  --pg-conf <file>         # Proguard configuration <file> (implies tree",
       "                           # shaking/minification).",
       "  --pg-map <file>          # Proguard map <file>.",
+      "  --print-mapping <file>   # Write name/line mapping to <file>.",
       "  --no-tree-shaking        # Force disable tree shaking of unreachable classes.",
       "  --no-discarded-checker   # Force disable the discarded checker (when tree shaking).",
       "  --no-minification        # Force disable minification of names.",
@@ -291,6 +299,7 @@
   private final boolean useDiscardedChecker;
   private final boolean useMinification;
   private final boolean ignoreMissingClasses;
+  private final Path printMappingFile;
 
   public static Builder builder() {
     return new Builder();
@@ -362,6 +371,8 @@
         builder.setProguardMapFile(Paths.get(args[++i]));
       } else if (arg.equals("--ignore-missing-classes")) {
         builder.setIgnoreMissingClasses(true);
+      } else if (arg.equals("--print-mapping")) {
+        builder.setPrintMappingFile(Paths.get(args[++i]));
       } else if (arg.startsWith("@")) {
         // TODO(zerny): Replace this with pipe reading.
         String argsFile = arg.substring(1);
@@ -404,7 +415,8 @@
       boolean useTreeShaking,
       boolean useDiscardedChecker,
       boolean useMinification,
-      boolean ignoreMissingClasses) {
+      boolean ignoreMissingClasses,
+      Path printMappingFile) {
     super(inputApp, outputPath, outputMode, mode, minApiLevel);
     assert proguardConfiguration != null;
     assert mainDexKeepRules != null;
@@ -416,6 +428,7 @@
     this.useDiscardedChecker = useDiscardedChecker;
     this.useMinification = useMinification;
     this.ignoreMissingClasses = ignoreMissingClasses;
+    this.printMappingFile = printMappingFile;
   }
 
   private R8Command(boolean printHelp, boolean printVersion) {
@@ -427,8 +440,8 @@
     useDiscardedChecker = false;
     useMinification = false;
     ignoreMissingClasses = false;
+    printMappingFile = null;
   }
-
   public boolean useTreeShaking() {
     return useTreeShaking;
   }
@@ -471,6 +484,7 @@
       // TODO(zerny): Should we support inlining in debug mode? b/62937285
       internal.inlineAccessors = false;
     }
+    internal.printMappingFile = printMappingFile;
     return internal;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/annotations/SynthesizedClassMap.java b/src/main/java/com/android/tools/r8/annotations/SynthesizedClassMap.java
new file mode 100644
index 0000000..b46a2ba
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/annotations/SynthesizedClassMap.java
@@ -0,0 +1,15 @@
+// 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.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.TYPE)
+public @interface SynthesizedClassMap {
+  Class<?>[] value() default {};
+}
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 1e70b9b..eba5613 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -133,7 +133,7 @@
 
     // Sort the class members.
     // Needed before adding static-value arrays and writing annotation directories and classes.
-    sortClassData(mixedSectionOffsets.getClassesWithData());
+    mixedSectionOffsets.getClassesWithData().forEach(DexProgramClass::sortMembers);
 
     // Add the static values for all fields now that we have committed to their sorting.
     mixedSectionOffsets.getClassesWithData().forEach(this::addStaticFieldValues);
@@ -254,23 +254,6 @@
     return Arrays.copyOf(dest.asArray(), layout.getEndOfFile());
   }
 
-  private void sortClassData(Collection<DexProgramClass> classesWithData) {
-    for (DexProgramClass clazz : classesWithData) {
-      sortEncodedFields(clazz.instanceFields());
-      sortEncodedFields(clazz.staticFields());
-      sortEncodedMethods(clazz.directMethods());
-      sortEncodedMethods(clazz.virtualMethods());
-    }
-  }
-
-  private void sortEncodedFields(DexEncodedField[] fields) {
-    Arrays.sort(fields, (DexEncodedField a, DexEncodedField b) -> a.field.compareTo(b.field));
-  }
-
-  private void sortEncodedMethods(DexEncodedMethod[] methods) {
-    Arrays.sort(methods, (DexEncodedMethod a, DexEncodedMethod b) -> a.method.compareTo(b.method));
-  }
-
   private void checkInterfaceMethods() throws ApiLevelException {
     for (DexProgramClass clazz : mapping.getClasses()) {
       if (clazz.isInterface()) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
index 1afd77e..3a8d56b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -13,7 +13,11 @@
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.graph.DexValue.DexValueType;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 public class DexAnnotation extends DexItem {
   public static final int VISIBILITY_BUILD = 0x00;
@@ -228,4 +232,37 @@
   private static DexValue toDexValue(String string, DexItemFactory factory) {
     return new DexValueString(factory.createString(string));
   }
+
+  public static Collection<DexType> readAnnotationSynthesizedClassMap(
+      DexProgramClass programClass,
+      DexItemFactory dexItemFactory) {
+    DexAnnotation foundAnnotation = programClass.annotations.getFirstMatching(
+        dexItemFactory.annotationSynthesizedClassMap);
+    if (foundAnnotation != null) {
+      DexAnnotationElement value = foundAnnotation.annotation.elements[0];
+      assert value.name.toSourceString().equals("value");
+      DexValueArray existingList = (DexValueArray) value.value;
+      Collection<DexType> synthesized = new ArrayList<>(existingList.values.length);
+      for (DexValue element : existingList.getValues()) {
+        synthesized.add(((DexValueType) element).value);
+      }
+      return synthesized;
+    }
+    return Collections.emptyList();
+  }
+
+  public static DexAnnotation createAnnotationSynthesizedClassMap(
+      Set<DexType> synthesized,
+      DexItemFactory dexItemFactory) {
+    DexValueType[] values = synthesized.stream()
+        .map(type -> new DexValueType(type))
+        .toArray(length -> new DexValueType[length]);
+    DexValueArray array = new DexValueArray(values);
+    DexAnnotationElement pair =
+        new DexAnnotationElement(dexItemFactory.createString("value"), array);
+    return new DexAnnotation(
+        VISIBILITY_BUILD,
+        new DexEncodedAnnotation(
+            dexItemFactory.annotationSynthesizedClassMap, new DexAnnotationElement[]{pair}));
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
index 00b99fa..93e103d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
@@ -74,8 +74,44 @@
     return null;
   }
 
+  public DexAnnotationSet getWithout(DexType annotationType) {
+    int index = 0;
+    for (DexAnnotation annotation : annotations) {
+      if (annotation.annotation.type == annotationType) {
+        DexAnnotation[] reducedArray = new DexAnnotation[annotations.length - 1];
+        System.arraycopy(annotations, 0, reducedArray, 0, index);
+        if (index < reducedArray.length) {
+          System.arraycopy(annotations, index + 1, reducedArray, index, reducedArray.length - index);
+        }
+        return new DexAnnotationSet(reducedArray);
+      }
+      ++index;
+    }
+    return this;
+  }
+
   private int sortedHashCode() {
     int hashCode = hashCode();
     return hashCode == UNSORTED ? 1 : hashCode;
   }
+
+  public DexAnnotationSet getWithAddedOrReplaced(DexAnnotation newAnnotation) {
+
+    // Check existing annotation for replacement.
+    int index = 0;
+    for (DexAnnotation annotation : annotations) {
+      if (annotation.annotation.type == newAnnotation.annotation.type) {
+        DexAnnotation[] modifiedArray = annotations.clone();
+        modifiedArray[index] = newAnnotation;
+        return new DexAnnotationSet(modifiedArray);
+      }
+      ++index;
+    }
+
+    // No existing annotation, append.
+    DexAnnotation[] extendedArray = new DexAnnotation[annotations.length + 1];
+    System.arraycopy(annotations, 0, extendedArray, 0, annotations.length);
+    extendedArray[annotations.length] = newAnnotation;
+    return new DexAnnotationSet(extendedArray);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index 0d2edcb..294b031 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -315,6 +315,7 @@
     DexString highestSortingString;
     private byte[] deadCode;
     private final Set<DexType> mainDexList = Sets.newIdentityHashSet();
+    private final Collection<DexProgramClass> synthesizedClasses;
 
     public Builder(DexItemFactory dexItemFactory, Timing timing) {
       this.programClasses = new ArrayList<>();
@@ -323,6 +324,7 @@
       this.deadCode = null;
       this.classpathClasses = null;
       this.libraryClasses = null;
+      this.synthesizedClasses = new ArrayList<>();
     }
 
     public Builder(DexApplication application) {
@@ -335,6 +337,7 @@
       dexItemFactory = application.dexItemFactory;
       mainDexList.addAll(application.mainDexList);
       deadCode = application.deadCode;
+      synthesizedClasses = new ArrayList<>();
     }
 
     public synchronized Builder setProguardMap(ClassNameMapper proguardMap) {
@@ -387,6 +390,7 @@
         DexProgramClass synthesizedClass, boolean addToMainDexList) {
       assert synthesizedClass.isProgramClass() : "All synthesized classes must be program classes";
       addProgramClass(synthesizedClass);
+      synthesizedClasses.add(synthesizedClass);
       if (addToMainDexList && !mainDexList.isEmpty()) {
         mainDexList.add(synthesizedClass.type);
       }
@@ -397,6 +401,14 @@
       return programClasses;
     }
 
+    public Collection<DexProgramClass> getSynthesizedClasses() {
+      return synthesizedClasses;
+    }
+
+    public Set<DexType> getMainDexList() {
+      return mainDexList;
+    }
+
     public Builder addToMainDexList(Collection<DexType> mainDexList) {
       this.mainDexList.addAll(mainDexList);
       return this;
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 527f27b..b7e41fe 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -161,6 +161,8 @@
   public final DexType annotationSourceDebugExtension = createType(
       "Ldalvik/annotation/SourceDebugExtension;");
   public final DexType annotationThrows = createType("Ldalvik/annotation/Throws;");
+  public final DexType annotationSynthesizedClassMap =
+      createType("Lcom/android/tools/r8/annotations/SynthesizedClassMap;");
 
   public synchronized void clearSubtypeInformation() {
     types.values().forEach(DexType::clearSubtypeInformation);
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index d27a3be..4332df8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -9,6 +9,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.function.Supplier;
@@ -213,6 +214,21 @@
     directMethods = newDirectMethods;
   }
 
+  public synchronized void sortMembers() {
+    sortEncodedFields(staticFields);
+    sortEncodedFields(instanceFields);
+    sortEncodedMethods(directMethods);
+    sortEncodedMethods(virtualMethods);
+  }
+
+  private void sortEncodedFields(DexEncodedField[] fields) {
+    Arrays.sort(fields, Comparator.comparing(a -> a.field));
+  }
+
+  private void sortEncodedMethods(DexEncodedMethod[] methods) {
+    Arrays.sort(methods, Comparator.comparing(a -> a.method));
+  }
+
   @Override
   public DexProgramClass get() {
     return this;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index c67e229..7dca5bd 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexApplication.Builder;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -39,16 +40,22 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.function.BiConsumer;
+import java.util.function.Function;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 public class IRConverter {
 
@@ -225,9 +232,72 @@
     synthesizeLambdaClasses(builder);
     desugarInterfaceMethods(builder, ExcludeDexResources);
 
+    if (options.intermediate) {
+      updateSynthesizedClassMapping(builder);
+    }
+
+    updateMainDexListWithSynthesizedClassMap(builder);
+
+    if (!options.intermediate) {
+      clearSynthesizedClassMapping(builder);
+    }
+
     return builder.build();
   }
 
+  private void updateMainDexListWithSynthesizedClassMap(Builder builder) {
+    Set<DexType> inputMainDexList = builder.getMainDexList();
+    if (!inputMainDexList.isEmpty()) {
+      Map<DexType, DexProgramClass> programClasses = builder.getProgramClasses().stream()
+          .collect(Collectors.toMap(
+            programClass -> programClass.type,
+            Function.identity()));
+      Collection<DexType> synthesized = new ArrayList<>();
+      for (DexType dexType : inputMainDexList) {
+        DexProgramClass programClass = programClasses.get(dexType);
+        if (programClass != null) {
+          synthesized.addAll(DexAnnotation.readAnnotationSynthesizedClassMap(
+              programClass, builder.dexItemFactory));
+        }
+      }
+      builder.addToMainDexList(synthesized);
+    }
+  }
+
+  private void clearSynthesizedClassMapping(Builder builder) {
+    for (DexProgramClass programClass : builder.getProgramClasses()) {
+      programClass.annotations =
+          programClass.annotations.getWithout(builder.dexItemFactory.annotationSynthesizedClassMap);
+    }
+  }
+
+  private void updateSynthesizedClassMapping(Builder builder) {
+    ListMultimap<DexProgramClass, DexProgramClass> originalToSynthesized =
+        ArrayListMultimap.create();
+    for (DexProgramClass synthesized : builder.getSynthesizedClasses()) {
+      for (DexProgramClass original : synthesized.getSynthesizedFrom()) {
+        originalToSynthesized.put(original, synthesized);
+      }
+    }
+
+    for (Map.Entry<DexProgramClass, Collection<DexProgramClass>> entry :
+        originalToSynthesized.asMap().entrySet()) {
+      DexProgramClass original = entry.getKey();
+      Set<DexType> synthesized = new HashSet<>();
+      entry.getValue()
+        .stream()
+        .map(dexProgramClass -> dexProgramClass.type)
+        .forEach(synthesized::add);
+      synthesized.addAll(
+          DexAnnotation.readAnnotationSynthesizedClassMap(original, builder.dexItemFactory));
+
+      DexAnnotation updatedAnnotation =
+          DexAnnotation.createAnnotationSynthesizedClassMap(synthesized, builder.dexItemFactory);
+
+      original.annotations = original.annotations.getWithAddedOrReplaced(updatedAnnotation);
+    }
+  }
+
   private void convertClassesToDex(Iterable<DexProgramClass> classes,
       ExecutorService executor) throws ExecutionException {
     List<Future<?>> futures = new ArrayList<>();
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 31a1724..c03c3d6 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -124,6 +124,8 @@
   // the code contains unsupported byte codes.
   public boolean skipReadingDexCode = false;
 
+  public Path printMappingFile = null;
+
   public void warningInvalidDebugInfo(DexEncodedMethod method, InvalidDebugInfoException e) {
     warningInvalidDebugInfoCount++;
   }
diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
index 7a56e3c..a2e64e1 100644
--- a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
@@ -6,14 +6,20 @@
 
 import static com.android.tools.r8.dex.Constants.ANDROID_K_API;
 import static com.android.tools.r8.dex.Constants.ANDROID_O_API;
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+import static com.android.tools.r8.utils.FileUtils.ZIP_EXTENSION;
 
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.OffOrAuto;
+import com.android.tools.r8.utils.OutputMode;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Collection;
+import java.util.HashSet;
 import java.util.function.UnaryOperator;
 import org.hamcrest.core.CombinableMatcher;
 import org.hamcrest.core.IsInstanceOf;
@@ -66,6 +72,10 @@
       }
     }
 
+    D8TestRunner withIntermediate(boolean intermediate) {
+      return withBuilderTransformation(builder -> builder.setIntermediate(intermediate));
+    }
+
     @Override
     D8TestRunner self() {
       return this;
@@ -519,6 +529,133 @@
         new Path[] {lib1Dex, lib3Dex, lib4Dex, testDex});
 
   }
+
+  @Test
+  public void testLambdaDesugaringWithMainDexList1() throws Throwable {
+    // Minimal case: there are synthesized classes but not form the main dex class.
+    testIntermediateWithMainDexList(
+        "lambdadesugaring",
+        1,
+        "lambdadesugaring.LambdaDesugaring$I");
+  }
+
+  @Test
+  public void testLambdaDesugaringWithMainDexList2() throws Throwable {
+    // Main dex class has many lambdas.
+    testIntermediateWithMainDexList("lambdadesugaring",
+        33,
+        "lambdadesugaring.LambdaDesugaring$Refs$B");
+  }
+
+  @Test
+  public void testInterfaceDesugaringWithMainDexList1() throws Throwable {
+    // Main dex interface has one static method.
+    testIntermediateWithMainDexList(
+        "interfacemethods",
+        Paths.get(ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR, "interfacemethods" + JAR_EXTENSION),
+        2,
+        "interfacemethods.I1");
+  }
+
+
+  @Test
+  public void testInterfaceDesugaringWithMainDexList2() throws Throwable {
+    // Main dex interface has one default method.
+    testIntermediateWithMainDexList(
+        "interfacemethods",
+        Paths.get(ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR, "interfacemethods" + JAR_EXTENSION),
+        2,
+        "interfacemethods.I2");
+  }
+
+  private void testIntermediateWithMainDexList(
+      String packageName,
+      int expectedMainDexListSize,
+      String... mainDexClasses)
+      throws Throwable {
+    testIntermediateWithMainDexList(
+        packageName,
+        Paths.get(EXAMPLE_DIR, packageName + JAR_EXTENSION),
+        expectedMainDexListSize,
+        mainDexClasses);
+  }
+
+    private void testIntermediateWithMainDexList(
+        String packageName,
+        Path input,
+        int expectedMainDexListSize,
+        String... mainDexClasses)
+        throws Throwable {
+    int minApi = ANDROID_K_API;
+
+    // Full build, will be used as reference.
+    TestRunner<?> full =
+        test(packageName + "full", packageName, "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(minApi)
+            .withOptionConsumer(option -> option.minimalMainDex = true)
+            .withMainDexClass(mainDexClasses);
+    Path fullDexes = temp.getRoot().toPath().resolve(packageName + "full" + ZIP_EXTENSION);
+    full.build(input, fullDexes);
+
+    // Builds with intermediate in both output mode.
+    Path dexesThroughIndexedIntermediate =
+        buildDexThroughIntermediate(packageName, input, OutputMode.Indexed, minApi, mainDexClasses);
+    Path dexesThroughFilePerInputClassIntermediate =
+        buildDexThroughIntermediate(packageName, input, OutputMode.FilePerInputClass, minApi,
+            mainDexClasses);
+
+    // Collect main dex types.
+    DexInspector fullInspector =  getMainDexInspector(fullDexes);
+    DexInspector indexedIntermediateInspector =
+        getMainDexInspector(dexesThroughIndexedIntermediate);
+    DexInspector filePerInputClassIntermediateInspector =
+        getMainDexInspector(dexesThroughFilePerInputClassIntermediate);
+    Collection<String> fullMainClasses = new HashSet<>();
+    fullInspector.forAllClasses(
+        clazz -> fullMainClasses.add(clazz.getFinalDescriptor()));
+    Collection<String> indexedIntermediateMainClasses = new HashSet<>();
+    indexedIntermediateInspector.forAllClasses(
+        clazz -> indexedIntermediateMainClasses.add(clazz.getFinalDescriptor()));
+    Collection<String> filePerInputClassIntermediateMainClasses = new HashSet<>();
+    filePerInputClassIntermediateInspector.forAllClasses(
+        clazz -> filePerInputClassIntermediateMainClasses.add(clazz.getFinalDescriptor()));
+
+    // Check.
+    Assert.assertEquals(expectedMainDexListSize, fullMainClasses.size());
+    Assert.assertEquals(fullMainClasses, indexedIntermediateMainClasses);
+    Assert.assertEquals(fullMainClasses, filePerInputClassIntermediateMainClasses);
+  }
+
+  private Path buildDexThroughIntermediate(
+      String packageName,
+      Path input,
+      OutputMode outputMode,
+      int minApi,
+      String... mainDexClasses)
+      throws Throwable {
+    TestRunner<?> intermediate =
+        test(packageName + "intermediate", packageName, "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(minApi)
+            .withOptionConsumer(option -> option.outputMode = outputMode)
+            .withIntermediate(true);
+    Path intermediateDex =
+        temp.getRoot().toPath().resolve(packageName + "intermediate" + ZIP_EXTENSION);
+    intermediate.build(input, intermediateDex);
+
+    TestRunner<?> end =
+        test(packageName + "dex", packageName, "N/A")
+            .withOptionConsumer(option -> option.minimalMainDex = true)
+            .withMainDexClass(mainDexClasses)
+            .withMinApiLevel(minApi);
+
+    Path dexesThroughIntermediate =
+        temp.getRoot().toPath().resolve(packageName + "dex" + ZIP_EXTENSION);
+    end.build(intermediateDex, dexesThroughIntermediate);
+    return dexesThroughIntermediate;
+  }
+
   @Override
   D8TestRunner test(String testName, String packageName, String mainClass) {
     return new D8TestRunner(testName, packageName, mainClass);
diff --git a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
index 8b6926e..932a61f 100644
--- a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
+++ b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
@@ -1732,19 +1732,23 @@
           // 1) t02
           // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Byte/serialization/Byte_serialization_A01.golden.0.ser
 
-          .put("lang.Byte.parseByteLjava_lang_StringI.Byte_parseByte_A02", any())
+          .put("lang.Byte.parseByteLjava_lang_StringI.Byte_parseByte_A02",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t01
           // java.lang.AssertionError: Parsed Byte instance from string:+1 radix:10
 
-          .put("lang.Byte.valueOfLjava_lang_StringI.Byte_valueOf_A02", any())
+          .put("lang.Byte.valueOfLjava_lang_StringI.Byte_valueOf_A02",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t01
           // java.lang.AssertionError: Parsed Byte instance from string:+1 radix:10
 
-          .put("lang.Byte.valueOfLjava_lang_String.Byte_ValueOf_A02", any())
+          .put("lang.Byte.valueOfLjava_lang_String.Byte_ValueOf_A02",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t02
           // java.lang.AssertionError: Parsed Byte instance from string:+1
 
-          .put("lang.Byte.decodeLjava_lang_String.Byte_decode_A04", any())
+          .put("lang.Byte.decodeLjava_lang_String.Byte_decode_A04",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t01
           // java.lang.AssertionError: Decoded Byte instance from string:+1
 
@@ -1760,11 +1764,13 @@
           // 2) t02
           // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/ClassCastException/serialization/ClassCastException_serialization_A01.golden.0.ser
 
-          .put("lang.Byte.ConstructorLjava_lang_String.Byte_Constructor_A02", any())
+          .put("lang.Byte.ConstructorLjava_lang_String.Byte_Constructor_A02",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t02
           // java.lang.AssertionError: Parsed Byte instance from string:+1
 
-          .put("lang.Byte.parseByteLjava_lang_String.Byte_parseByte_A02", any())
+          .put("lang.Byte.parseByteLjava_lang_String.Byte_parseByte_A02",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t02
           // java.lang.AssertionError: Parsed Byte instance from string:+1
 
@@ -1833,7 +1839,8 @@
           // 1) t02
           // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Boolean/serialization/Boolean_serialization_A01.golden.0.ser
 
-          .put("lang.Integer.valueOfLjava_lang_StringI.Integer_valueOf_A02", any())
+          .put("lang.Integer.valueOfLjava_lang_StringI.Integer_valueOf_A02",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t07
           // java.lang.AssertionError: NumberFormatException expected for input: +1 and radix: 10
 
@@ -1841,40 +1848,48 @@
           // 1) t02
           // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Integer/serialization/Integer_serialization_A01.golden.0.ser
 
-          .put("lang.Integer.parseIntLjava_lang_String.Integer_parseInt_A02", any())
+          .put("lang.Integer.parseIntLjava_lang_String.Integer_parseInt_A02",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t06
           // java.lang.AssertionError: Expected exception: java.lang.NumberFormatException
 
-          .put("lang.Integer.getIntegerLjava_lang_StringI.Integer_getInteger_A02", any())
+          .put("lang.Integer.getIntegerLjava_lang_StringI.Integer_getInteger_A02",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t03
           // java.lang.AssertionError: expected:<6031769> but was:<1>
 
-          .put("lang.Integer.valueOfLjava_lang_String.Integer_valueOf_A02", any())
+          .put("lang.Integer.valueOfLjava_lang_String.Integer_valueOf_A02",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t07
           // java.lang.AssertionError: NumberFormatException expected for input: +1
 
-          .put("lang.Integer.decodeLjava_lang_String.Integer_decode_A04", any())
+          .put("lang.Integer.decodeLjava_lang_String.Integer_decode_A04",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t06
           // java.lang.AssertionError: Expected exception: java.lang.NumberFormatException
 
-          .put("lang.Integer.parseIntLjava_lang_StringI.Integer_parseInt_A02", any())
+          .put("lang.Integer.parseIntLjava_lang_StringI.Integer_parseInt_A02",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t06
           // java.lang.AssertionError: Expected exception: java.lang.NumberFormatException
 
           .put("lang.Integer.getIntegerLjava_lang_StringLjava_lang_Integer.Integer_getInteger_A02",
-              any())
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t03
           // java.lang.AssertionError: expected:<6031769> but was:<1>
 
-          .put("lang.Integer.ConstructorLjava_lang_String.Integer_Constructor_A02", any())
+          .put("lang.Integer.ConstructorLjava_lang_String.Integer_Constructor_A02",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t06
           // java.lang.AssertionError: Expected exception: java.lang.NumberFormatException
 
-          .put("lang.Integer.getIntegerLjava_lang_String.Integer_getInteger_A02", any())
+          .put("lang.Integer.getIntegerLjava_lang_String.Integer_getInteger_A02",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t03
           // java.lang.AssertionError: expected null, but was:<1>
 
-          .put("lang.ref.PhantomReference.isEnqueued.PhantomReference_isEnqueued_A01", any())
+          .put("lang.ref.PhantomReference.isEnqueued.PhantomReference_isEnqueued_A01",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t04
           // java.lang.AssertionError: reference is not enqueued after 2 sec
 
@@ -1898,7 +1913,8 @@
           // 1) t03
           // java.lang.AssertionError: expected null, but was:<[I@1b32f32>
 
-          .put("lang.ref.WeakReference.isEnqueued.WeakReference_isEnqueued_A01", any())
+          .put("lang.ref.WeakReference.isEnqueued.WeakReference_isEnqueued_A01",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t03
           // java.lang.AssertionError: reference is not enqueued after 2 sec
 
@@ -2003,11 +2019,13 @@
           // 2) t04
           // org.junit.ComparisonFailure: expected:<0.001[0]> but was:<0.001[]>
 
-          .put("lang.Short.valueOfLjava_lang_StringI.Short_valueOf_A02", any())
+          .put("lang.Short.valueOfLjava_lang_StringI.Short_valueOf_A02",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t03
           // java.lang.AssertionError: Missing NumberFormatException for radix=10
 
-          .put("lang.Short.valueOfLjava_lang_String.Short_valueOf_A02", any())
+          .put("lang.Short.valueOfLjava_lang_String.Short_valueOf_A02",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t03
           // java.lang.AssertionError: Missing NumberFormatException for arg="+1"
 
@@ -2015,15 +2033,18 @@
           // 1) t02
           // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Short/serialization/Short_serialization_A01.golden.0.ser
 
-          .put("lang.Short.parseShortLjava_lang_String.Short_parseShort_A02", any())
+          .put("lang.Short.parseShortLjava_lang_String.Short_parseShort_A02",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t01
           // java.lang.AssertionError: Parsed Short instance from string:+1
 
-          .put("lang.Short.decodeLjava_lang_String.Short_decode_A04", any())
+          .put("lang.Short.decodeLjava_lang_String.Short_decode_A04",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t01
           // java.lang.AssertionError: Decoded Short instance from string:+1
 
-          .put("lang.Short.ConstructorLjava_lang_String.Short_Constructor_A02", any())
+          .put("lang.Short.ConstructorLjava_lang_String.Short_Constructor_A02",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t02
           // java.lang.AssertionError: Created Short instance from string:+1
 
@@ -2042,7 +2063,8 @@
           // 2) t02
           // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/annotation/AnnotationFormatError/serialization/AnnotationFormatError_serialization_A01.golden.0.ser
 
-          .put("lang.Short.parseShortLjava_lang_StringI.Short_parseShort_A02", any())
+          .put("lang.Short.parseShortLjava_lang_StringI.Short_parseShort_A02",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t01
           // java.lang.AssertionError: Parsed Short instance from string:+1 radix:10
 
@@ -2261,7 +2283,8 @@
           // 1) t04
           // java.lang.AssertionError: expected null, but was:<class com.google.jctf.test.lib.java.lang.Class.getDeclaringClass.Class_getDeclaringClass_A01>
 
-          .put("lang.Class.getDeclaredFields.Class_getDeclaredFields_A01", any())
+          .put("lang.Class.getDeclaredFields.Class_getDeclaredFields_A01",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t02
           // java.lang.AssertionError: array lengths differed, expected.length=0 actual.length=2
 
@@ -2372,7 +2395,7 @@
           // java.lang.AssertionError: Vague error message
 
           .put("lang.Class.getClasses.Class_getClasses_A01",
-              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_4_4_4)))
           // 1) t03
           // java.lang.AssertionError: Array lengths expected:<2> but was:<3>
 
@@ -2548,7 +2571,7 @@
           // Caused by: java.lang.AssertionError: Unable to configure default providers
 
           .put("lang.Class.getMethodLjava_lang_String_Ljava_lang_Class.Class_getMethod_A01",
-              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_4_4_4)))
           // 1) t04
           // java.lang.AssertionError: expected:<interface com.google.jctf.test.lib.java.lang.Class.getMethodLjava_lang_String_Ljava_lang_Class.Class_getMethod_A01$I1> but was:<interface com.google.jctf.test.lib.java.lang.Class.getMethodLjava_lang_String$Ljava_lang_Class.Class_getMethod_A01$I2>
 
@@ -2676,7 +2699,7 @@
           // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/String/CASE_INSENSITIVE_ORDER/serialization/String_serialization_A01.golden.0.ser
 
           .put("lang.String.getBytesLjava_lang_String.String_getBytes_A14",
-              match(runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t07
           // arrays first differed at element [0]; expected:<-2> but was:<-1>
           // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
@@ -2737,7 +2760,7 @@
           // org.junit.ComparisonFailure: Incorrect double string returned expected:<0.001[0]> but was:<0.001[]>
 
           .put("lang.String.getBytesLjava_nio_charset_Charset.String_getBytes_A14",
-              match(runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t07
           // arrays first differed at element [0]; expected:<-2> but was:<-1>
           // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
@@ -3600,7 +3623,7 @@
 
           .put(
               "lang.reflect.Proxy.getInvocationHandlerLjava_lang_Object.Proxy_getInvocationHandler_A02",
-              any())
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t02
           // java.lang.Exception: Unexpected exception, expected<java.lang.IllegalArgumentException> but was<java.lang.NullPointerException>
           // Caused by: java.lang.NullPointerException
@@ -3611,6 +3634,16 @@
           // 2) t05
           // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
 
+          .put("lang.reflect.Proxy.Class.Proxy_class_A02", match(runtimes(DexVm.ART_4_4_4)))
+          // 1) t02
+          // java.lang.AssertionError: expected array was null
+          // 2) t04
+          // java.lang.AssertionError: expected array was null
+
+          .put("lang.reflect.Proxy.Class.Proxy_class_A03", match(runtimes(DexVm.ART_4_4_4)))
+          // 1) t03
+          // java.lang.AbstractMethodError: abstract method not implemented
+
           .put(
               "lang.reflect.Proxy.getProxyClassLjava_lang_ClassLoader_Ljava_lang_Class.Proxy_getProxyClass_A01",
               any())
@@ -3775,6 +3808,12 @@
           // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.MissingParameterTypeMethod" on path: DexPathList[[dex file "/tmp/junit3534060116722105133/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
 
           .put(
+              "lang.reflect.InvocationHandler.invokeLjava_lang_ObjectLjava_lang_reflect_Method_Ljava_lang_Object.InvocationHandler_invoke_A01",
+              match(runtimes(DexVm.ART_4_4_4)))
+          // 1) t04
+          // java.lang.AssertionError: expected array was null
+
+          .put(
               "lang.reflect.InvocationHandler.invokeLjava_lang_ObjectLjava_lang_reflect_Method_Ljava_lang_Object.InvocationHandler_invoke_A02",
               any())
           // 1) t04
@@ -3784,6 +3823,11 @@
           // 1) t01
           // java.lang.AssertionError: Expected exception: java.lang.TypeNotPresentException
 
+          .put("lang.reflect.Method.hashCode.Method_hashCode_A01",
+              match(runtimes(DexVm.ART_4_4_4)))
+          // 1)
+          // java.lang.AssertionError: expected:<-1918826964> but was:<-1295482945>
+
           .put("lang.reflect.Method.toString.Method_toString_A01", any())
           // 1) t04
           // org.junit.ComparisonFailure: expected:<public static final [synchronized ]native void com.goog...> but was:<public static final []native void com.goog...>
@@ -3838,10 +3882,20 @@
           // 1) t02
           // java.lang.AssertionError: Exception is not thrown: field: bytePublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@b1b0f3d
 
+          .put("lang.reflect.Field.setByteLjava_lang_ObjectB.Field_setByte_A02",
+              match(runtimes(DexVm.ART_4_4_4)))
+          // 1) t01
+          // java.lang.AssertionError: Illegal exception is thrown: java.lang.IllegalAccessException: field is marked 'final', field: bytePublicField, class: class com.google.jctf.test.lib.java.lang.reflect.Field.TestFinalPrimitiveField
+
           .put("lang.reflect.Field.setBooleanLjava_lang_ObjectZ.Field_setBoolean_A01", any())
           // 1) t02
           // java.lang.AssertionError: Exception is not thrown: field: booleanPublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@953cc4f
 
+          .put("lang.reflect.Field.setBooleanLjava_lang_ObjectZ.Field_setBoolean_A02",
+              match(runtimes(DexVm.ART_4_4_4)))
+          // 1) t01
+          // java.lang.AssertionError: Illegal exception is thrown: java.lang.IllegalAccessException: field is marked 'final', field: booleanPublicField, class: class com.google.jctf.test.lib.java.lang.reflect.Field.TestFinalPrimitiveField
+
           .put("lang.reflect.Field.setCharLjava_lang_ObjectC.Field_setChar_A05", any())
           // 1) t01
           // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
@@ -3869,6 +3923,11 @@
           // 1) t02
           // java.lang.AssertionError: Exception is not thrown: field: charPublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@95271f1
 
+          .put("lang.reflect.Field.setCharLjava_lang_ObjectC.Field_setChar_A02",
+              match(runtimes(DexVm.ART_4_4_4)))
+          // 1) t01
+          // java.lang.AssertionError: Illegal exception is thrown: java.lang.IllegalAccessException: field is marked 'final', field: charPublicField, class: class com.google.jctf.test.lib.java.lang.reflect.Field.TestFinalPrimitiveField
+
           .put("lang.reflect.Field.getDoubleLjava_lang_Object.Field_getDouble_A05", any())
           // 1) t01
           // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
@@ -3880,12 +3939,22 @@
           // 1) t02
           // java.lang.AssertionError: Exception is not thrown: field: floatPublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@3fca927
 
+          .put("lang.reflect.Field.setFloatLjava_lang_ObjectF.Field_setFloat_A02",
+              match(runtimes(DexVm.ART_4_4_4)))
+          // 1) t01
+          // java.lang.AssertionError: Illegal exception is thrown: java.lang.IllegalAccessException: field is marked 'final', field: floatPublicField, class: class com.google.jctf.test.lib.java.lang.reflect.Field.TestFinalPrimitiveField
+
           .put("lang.reflect.Field.getAnnotationLjava_lang_Class.Field_getAnnotation_A01", any())
           // 1) t02
           // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
           // 2) t03
           // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
 
+          .put("lang.reflect.Field.setIntLjava_lang_ObjectI.Field_setInt_A02",
+              match(runtimes(DexVm.ART_4_4_4)))
+          // 1) t01
+          // java.lang.AssertionError: Illegal exception is thrown: java.lang.IllegalAccessException: field is marked 'final', field: intPublicField, class: class com.google.jctf.test.lib.java.lang.reflect.Field.TestFinalPrimitiveField
+
           .put("lang.reflect.Field.getIntLjava_lang_Object.Field_getInt_A05", any())
           // 1) t01
           // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
@@ -3950,6 +4019,11 @@
           // 1) t02
           // java.lang.AssertionError: Exception is not thrown: field: intPublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@94fbd35
 
+          .put("lang.reflect.Field.setDoubleLjava_lang_ObjectD.Field_setDouble_A02",
+              match(runtimes(DexVm.ART_4_4_4)))
+          // 1) t01
+          // java.lang.AssertionError: Illegal exception is thrown: java.lang.IllegalAccessException: field is marked 'final', field: doublePublicField, class: class com.google.jctf.test.lib.java.lang.reflect.Field.TestFinalPrimitiveField
+
           .put("lang.reflect.Field.setDoubleLjava_lang_ObjectD.Field_setDouble_A05", any())
           // 1) t01
           // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
@@ -3961,6 +4035,11 @@
           // 1) t02
           // java.lang.AssertionError: Exception is not thrown: field: shortPublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@7887a47
 
+          .put("lang.reflect.Field.setLongLjava_lang_ObjectJ.Field_setLong_A02",
+              match(runtimes(DexVm.ART_4_4_4)))
+          // 1) t01
+          // java.lang.AssertionError: Illegal exception is thrown: java.lang.IllegalAccessException: field is marked 'final', field: longPublicField, class: class com.google.jctf.test.lib.java.lang.reflect.Field.TestFinalPrimitiveField
+
           .put("lang.reflect.Field.setLongLjava_lang_ObjectJ.Field_setLong_A05", any())
           // 1) t01
           // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
@@ -3976,6 +4055,11 @@
           // 1) t02
           // java.lang.AssertionError: Exception is not thrown: field: doublePublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@4dd95e2
 
+          .put("lang.reflect.Field.setShortLjava_lang_ObjectS.Field_setShort_A02",
+              match(runtimes(DexVm.ART_4_4_4)))
+          // 1) t01
+          // java.lang.AssertionError: Illegal exception is thrown: java.lang.IllegalAccessException: field is marked 'final', field: shortPublicField, class: class com.google.jctf.test.lib.java.lang.reflect.Field.TestFinalPrimitiveField
+
           .put("lang.reflect.Field.setShortLjava_lang_ObjectS.Field_setShort_A05", any())
           // 1) t01
           // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
@@ -3997,6 +4081,11 @@
           // 2) t02
           // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
 
+          .put("lang.reflect.Field.setLjava_lang_ObjectLjava_lang_Object.Field_set_A02",
+              match(runtimes(DexVm.ART_4_4_4)))
+          // 1) t01
+          // java.lang.AssertionError: Illegal exception is thrown: java.lang.IllegalAccessException: field is marked 'final', field: bytePublicField, class: class com.google.jctf.test.lib.java.lang.reflect.Field.TestFinalPrimitiveField
+
           .put("lang.reflect.Field.setLjava_lang_ObjectLjava_lang_Object.Field_set_A05", any())
           // 1) t01
           // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
@@ -4365,7 +4454,7 @@
 
           .put(
               "util.concurrent.ConcurrentHashMap.serialization.ConcurrentHashMap_serialization_A01",
-              any())
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t01
           // java.lang.AssertionError: Unable to configure default providers
 
@@ -4438,57 +4527,57 @@
           // java.lang.AssertionError: no IllegalThreadStateException 1
 
           .put("lang.String.getBytesLjava_lang_String.String_getBytes_A02",
-              match(runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t01(com.google.jctf.test.lib.java.lang.String.getBytesLjava_lang_String.String_getBytes_A02)
           // java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<java.io.UnsupportedEncodingException>
 
           .put(
               "util.concurrent.CopyOnWriteArrayList.lastIndexOfLjava_lang_ObjectI.CopyOnWriteArrayList_lastIndexOf_A02",
-              match(runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // java.lang.AssertionError: Expected exception: java.lang.IndexOutOfBoundsException
 
           .put(
               "util.concurrent.CopyOnWriteArrayList.lastIndexOfLjava_lang_ObjectI.CopyOnWriteArrayList_lastIndexOf_A01",
-              match(runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t01(com.google.jctf.test.lib.java.util.concurrent.CopyOnWriteArrayList.lastIndexOfLjava_lang_ObjectI.CopyOnWriteArrayList_lastIndexOf_A01)
           // java.lang.AssertionError: expected:<3> but was:<1>
           // 2) t02(com.google.jctf.test.lib.java.util.concurrent.CopyOnWriteArrayList.lastIndexOfLjava_lang_ObjectI.CopyOnWriteArrayList_lastIndexOf_A01)
           // java.lang.ArrayIndexOutOfBoundsException: length=3; index=2147483647
 
           .put("lang.StringBuffer.getCharsII_CI.StringBuffer_getChars_A03",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t03
           // java.lang.NullPointerException: dst == null
 
           .put("lang.StringBuffer.appendF.StringBuffer_append_A01",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t02
           // java.lang.AssertionError: Buffer is invalid length after append expected:<26> but was:<25>
 
           .put("lang.StringBuffer.insertI_CII.StringBuffer_insert_A02",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t01
           // java.lang.NullPointerException: Attempt to get length of null array
 
           .put("lang.StrictMath.scalbDI.StrictMath_scalb_A03",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t01
           // java.lang.AssertionError: Wrong result provided for argument: -1.7976931348623157E308 scaleFactor: 2147483647 expected:<-Infinity> but was:<-0.0>
 
           .put("lang.StrictMath.scalbDI.StrictMath_scalb_A01",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t03
           // java.lang.AssertionError: Wrong result provided for argument: -2.2250738585072014E-308 scaleFactor: -2147483647 expected:<-0.0> but was:<-Infinity>
           // 2) t04
           // java.lang.AssertionError: Wrong result provided for argument: 1.7976931348623157E308 scaleFactor: -2046 expected:<2.2250738585072014E-308> but was:<2.225073858507201E-308>
 
           .put("lang.StrictMath.scalbFI.StrictMath_scalb_A03",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t01
           // java.lang.AssertionError: Wrong result provided for argument: -3.4028235E38 scaleFactor: 2147483647 expected:<-Infinity> but was:<-0.0>
 
           .put("lang.StrictMath.scalbFI.StrictMath_scalb_A01",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t02
           // java.lang.AssertionError: Wrong result provided for argument: 3.4028235E38 scaleFactor: -254 expected:<1.1754943508222875E-38> but was:<1.1754942106924411E-38>
           // 2) t03
@@ -4496,96 +4585,96 @@
 
           .put(
               "lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_StringJ.Thread_Constructor_A07",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t02
           // java.lang.AssertionError: wrong daemonism expected:<true> but was:<false>
 
           .put(
               "lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_String.Thread_Constructor_A07",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t02
           // java.lang.AssertionError: wrong daemonism expected:<true> but was:<false>
 
           .put("lang.Thread.toString.Thread_toString_A01",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t02
           // java.lang.AssertionError
 
           .put("lang.Thread.start.Thread_start_A02",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t01
           // java.lang.IllegalThreadStateException: Thread group still contains threads: start
 
           .put("lang.Thread.setPriorityI.Thread_setPriority_A01",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t02
           // java.lang.AssertionError: expected:<5> but was:<10>
 
           .put("lang.ClassLoader.ConstructorLjava_lang_ClassLoader.ClassLoader_Constructor_A01",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t01
           // java.lang.NullPointerException: parentLoader == null && !nullAllowed
           // 2) t03
           // java.lang.NullPointerException: parentLoader == null && !nullAllowed
 
           .put("lang.Enum.compareToLjava_lang_Enum.Enum_compareTo_A03",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t01
           // java.lang.AssertionError: Expected exception: java.lang.ClassCastException
 
           .put("lang.Enum.hashCode.Enum_hashCode_A01",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t02
           // java.lang.AssertionError
 
           .put("lang.StackTraceElement.hashCode.StackTraceElement_hashCode_A01",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t02
           // java.lang.AssertionError
 
           .put("lang.ProcessBuilder.environment.ProcessBuilder_environment_A01",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t01
           // java.lang.AssertionError: should throw ClassCastException.
 
           .put("lang.ProcessBuilder.environment.ProcessBuilder_environment_A03",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t01
           // java.lang.AssertionError: should throw ClassCastException.
 
           .put("lang.Float.toStringF.Float_toString_A04",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t02
           // org.junit.ComparisonFailure: Invalid string produced for bits: 4efffffa expected:<2.147482[88]E9> but was:<2.147482[9]E9>
 
           .put("lang.Float.toStringF.Float_toString_A03",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t02
           // org.junit.ComparisonFailure: expected:<-1.175494[35]E-38> but was:<-1.175494[4]E-38>
 
           .put("lang.ThreadGroup.getMaxPriority.ThreadGroup_getMaxPriority_A01",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t01
           // java.lang.AssertionError: New value should be the same as we set expected:<2> but was:<1>
 
           .put(
               "lang.ThreadGroup.uncaughtExceptionLjava_lang_ThreadLjava_lang_Throwable.ThreadGroup_uncaughtException_A02",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t05
           // java.lang.AssertionError: Non-informative exception info: java.lang.RuntimeException
 
           .put("lang.ThreadGroup.list.ThreadGroup_list_A01",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t04
           // java.lang.IllegalThreadStateException: Thread group still contains threads: Test group(list)
 
           .put("lang.ThreadGroup.setMaxPriorityI.ThreadGroup_setMaxPriority_A01",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t01
           // java.lang.IllegalThreadStateException: Thread group still contains threads: Test root(setMaxPriority)
 
           .put("lang.ThreadGroup.setMaxPriorityI.ThreadGroup_setMaxPriority_A04",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t01
           // java.lang.AssertionError: New value should be the same as we set expected:<2> but was:<1>
           // 2) t02
@@ -4594,40 +4683,46 @@
           // java.lang.AssertionError: expected:<7> but was:<1>
 
           .put("lang.ThreadGroup.toString.ThreadGroup_toString_A01",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t01
           // org.junit.ComparisonFailure: toString does not follow the RI expected:<... group(toString),max[pri]=10]> but was:<... group(toString),max[Priority]=10]>
 
           .put("lang.Class.getFieldLjava_lang_String.Class_getField_A01",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t04
           // java.lang.AssertionError: expected:<interface com.google.jctf.test.lib.java.lang.Class.getFieldLjava_lang_String.Class_getField_A01$I1> but was:<class com.google.jctf.test.lib.java.lang.Class.getFieldLjava_lang_String.Class_getField_A01$S1>
 
           .put("lang.String.replaceCC.String_replace_A01",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t04
           // java.lang.AssertionError: expected same:<aaaaaa> was not:<aaaaaa>
 
           .put("lang.Package.isCompatibleWithLjava_lang_String.Package_isCompatibleWith_A02",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t01
           // java.lang.AssertionError: NumberFormatException isn't thrown for desired . and current 1.0
           // 2) t03
           // java.lang.AssertionError: NumberFormatException isn't thrown for desired 1.0 and current .
 
           .put("lang.StringBuilder.appendF.StringBuilder_append_A01",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t01
           // java.lang.AssertionError: Invalid length of created builder expected:<14> but was:<13>
 
           .put("lang.StringBuilder.insertIF.StringBuilder_insert_A01",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t01
           // java.lang.AssertionError: Invalid length of created builder expected:<14> but was:<13>
 
           .put(
+              "lang.reflect.AccessibleObject.setAccessibleZ.AccessibleObject_setAccessible_A04",
+              match(runtimes(DexVm.ART_4_4_4)))
+          // 1) t01
+          // java.lang.AssertionError: SecurityException expected.
+
+          .put(
               "lang.reflect.AccessibleObject.setAccessible_Ljava_lang_reflect_AccessibleObjectZ.AccessibleObject_setAccessible_A04",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t01
           // java.lang.AssertionError: SecurityException expected.
           // 2) t02
@@ -4638,12 +4733,12 @@
           // java.lang.AssertionError: SecurityException expected.
 
           .put("lang.Character.UnicodeBlock.forName_java_lang_String.UnicodeBlock_forName_A03",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t01
           // java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
 
           .put("lang.System.loadLjava_lang_String.System_load_A02",
-              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t03
           // java.lang.AssertionError: Expected exception: java.lang.UnsatisfiedLinkError
 
@@ -4654,7 +4749,7 @@
           // 2) t02
           // java.lang.AssertionError: Wrong value returned for start: -0.0 direction: NaN expected:<-1.4E-45> but was:<NaN>
 
-          .put("lang.Math.hypotDD.Math_hypot_A04", match(runtimes(DexVm.ART_5_1_1)))
+          .put("lang.Math.hypotDD.Math_hypot_A04", match(runtimes(DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t04
           // java.lang.AssertionError
 
@@ -4698,6 +4793,12 @@
           // 1) t02
           // java.lang.AssertionError: java.lang.AssertionError: expected:<7> but was:<6>
 
+          .put("lang.ref.PhantomReference.isEnqueued.PhantomReference_isEnqueued_A01",
+              match(runtimes(DexVm.ART_4_4_4)))
+          .put("lang.ref.WeakReference.isEnqueued.WeakReference_isEnqueued_A01",
+              match(runtimes(DexVm.ART_4_4_4)))
+          // Passes or fails randomly. Check that something is enqueued after 2 seconds.
+
           .build(); // end of flakyWithArt
 
   public static final Multimap<String, TestCondition> timeoutsWithArt =
diff --git a/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java b/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
index 57bc26c..2de3624 100644
--- a/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
@@ -7,6 +7,7 @@
 
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import java.io.File;
@@ -78,40 +79,48 @@
   @Parameters(name = "{0}")
   public static Collection<String[]> data() {
     return Arrays.asList(new String[][]{
-        {"arithmetic", "-1\n3\n2\n3\n3.0\n1\n0\n-131580\n-131580\n2\n4\n-2\n"},
-        {"controlflow", "2\n1\n2\n1\n2\n1\n2\n1\n2\n1\n2\n1\n2\n"},
-        {"fibonacci", "55\n55\n55\n55\n"},
+        {"arithmetic",
+            StringUtils.lines("-1", "3", "2", "3", "3.0", "1", "0", "-131580", "-131580", "2", "4",
+                "-2")},
+        {"controlflow",
+            StringUtils.lines("2", "1", "2", "1", "2", "1", "2", "1", "2", "1", "2", "1", "2")},
+        {"fibonacci", StringUtils.lines("55", "55", "55", "55")},
         {"fill-array-data", "[1, 2, 3][4, 5, 6]"},
         {"filled-new-array", "[1, 2, 3][4, 5, 6][1, 2, 3, 4, 5, 6][6, 5, 4, 3, 2, 1]"},
         {"packed-switch", "12345"},
         {"sparse-switch", "12345"},
         {"unreachable-code-1", "777"},
-        {"multiple-returns", "TFtf\n1\n4611686018427387904\ntrue\nfalse\n"},
+        {"multiple-returns",
+            StringUtils.lines("TFtf", "1", "4611686018427387904", "true", "false")},
         {"try-catch", ""},
-        {"phi-removal-regression", "returnBoolean\n"},
-        {"overlapping-long-registers", "-9151314442816847872\n-9151314442816319488\n"},
-        {"type-confusion-regression", "java.lang.RuntimeException: Test.<init>()\n"},
+        {"phi-removal-regression", StringUtils.lines("returnBoolean")},
+        {"overlapping-long-registers",
+            StringUtils.lines("-9151314442816847872", "-9151314442816319488")},
+        {"type-confusion-regression",
+            StringUtils.lines("java.lang.RuntimeException: Test.<init>()")},
         {"type-confusion-regression2",
-            "java.lang.NullPointerException: Attempt to read from null array\n"},
+            StringUtils.lines("java.lang.NullPointerException: Attempt to read from null array")},
         {"type-confusion-regression3",
-            "java.lang.NullPointerException: Attempt to read from field 'byte[] Test.a'" +
-                " on a null object reference\n"},
+            StringUtils.lines(
+                "java.lang.NullPointerException: Attempt to read from field 'byte[] Test.a'" +
+                    " on a null object reference")},
         {"type-confusion-regression4", ""},
-        {"type-confusion-regression5", "java.lang.RuntimeException: getId()I\n"},
-        {"chain-of-loops", "java.lang.RuntimeException: f(II)\n"},
-        {"new-instance-and-init", "Test(0)\nTest(0)\nTest(0)\n"},
+        {"type-confusion-regression5", StringUtils.lines("java.lang.RuntimeException: getId()I")},
+        {"chain-of-loops", StringUtils.lines("java.lang.RuntimeException: f(II)")},
+        {"new-instance-and-init", StringUtils.lines("Test(0)", "Test(0)", "Test(0)")},
         {"bad-codegen",
-            "java.lang.NullPointerException: Attempt to read from field " +
-                "'Test Test.a' on a null object reference\n"},
-        {"merge-blocks-regression", "java.lang.NullPointerException: Attempt to invoke virtual"
-            + " method 'Test Test.bW_()' on a null object reference\n"},
-        {"self-is-catch-block", "100\n-1\n"},
+            StringUtils.lines("java.lang.NullPointerException: Attempt to read from field " +
+                "'Test Test.a' on a null object reference")},
+        {"merge-blocks-regression",
+            StringUtils.lines("java.lang.NullPointerException: Attempt to invoke virtual"
+                + " method 'Test Test.bW_()' on a null object reference")},
+        {"self-is-catch-block", StringUtils.lines("100", "-1")},
         {"infinite-loop", ""},
         {"regression/33336471",
-            "START\n0\n2\nLOOP\n1\n2\nLOOP\n2\n2\nDONE\n" +
-                "START\n0\n2\nLOOP\n1\n2\nLOOP\n2\n2\nDONE\n"},
+            StringUtils.lines("START", "0", "2", "LOOP", "1", "2", "LOOP", "2", "2", "DONE",
+                "START", "0", "2", "LOOP", "1", "2", "LOOP", "2", "2", "DONE")},
         {"regression/33846227", ""},
-        {"illegal-invokes", "ICCE\nICCE\n"},
+        {"illegal-invokes", StringUtils.lines("ICCE", "ICCE")},
     });
   }
 
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index 7a0e793..0e0fd9d 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -12,17 +12,20 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.FoundClassSubject;
 import com.android.tools.r8.utils.DexInspector.FoundMethodSubject;
 import com.android.tools.r8.utils.DexInspector.InstructionSubject;
 import com.android.tools.r8.utils.DexInspector.InvokeInstructionSubject;
+import com.android.tools.r8.utils.FileUtils;
 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 com.google.common.io.ByteStreams;
 import java.io.IOException;
+import java.io.InputStream;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
@@ -30,10 +33,13 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ExecutionException;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.function.UnaryOperator;
 import java.util.stream.Collectors;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
@@ -94,6 +100,10 @@
       return self();
     }
 
+    C withMainDexClass(String... classes) {
+      return withBuilderTransformation(builder -> builder.addMainDexClasses(classes));
+    }
+
     C withInterfaceMethodDesugaring(OffOrAuto behavior) {
       return withOptionConsumer(o -> o.interfaceMethodDesugaring = behavior);
     }
@@ -386,8 +396,18 @@
           "JVM output does not match art output.\n\tjvm: "
               + javaResult.stdout
               + "\n\tart: "
-              + output,
-          output.equals(javaResult.stdout));
+              + output.replace("\r", ""),
+          output.equals(javaResult.stdout.replace("\r", "")));
+    }
+  }
+
+  protected DexInspector getMainDexInspector(Path zip)
+      throws ZipException, IOException, ExecutionException {
+    try (ZipFile zipFile = new ZipFile(zip.toFile())) {
+      try (InputStream in =
+          zipFile.getInputStream(zipFile.getEntry(FileUtils.DEFAULT_DEX_FILENAME))) {
+        return new DexInspector(AndroidApp.fromDexProgramData(ByteStreams.toByteArray(in)));
+      }
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 2271a02..94d52f0 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -27,6 +27,7 @@
 import com.google.common.collect.Lists;
 import com.google.common.io.ByteStreams;
 import com.google.common.io.CharStreams;
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -60,6 +61,7 @@
   public static final String SMALI_BUILD_DIR = BUILD_DIR + "test/smali/";
 
   public static final String LINE_SEPARATOR = StringUtils.LINE_SEPARATOR;
+  public final static String PATH_SEPARATOR = File.pathSeparator;
 
   private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
   private static final int DEFAULT_MIN_SDK = Constants.ANDROID_I_API;
@@ -601,7 +603,7 @@
 
   public static ProcessResult runJava(List<String> classpath, String mainClass) throws IOException {
     ProcessBuilder builder = new ProcessBuilder(
-        getJavaExecutable(), "-cp", String.join(":", classpath), mainClass);
+        getJavaExecutable(), "-cp", String.join(PATH_SEPARATOR, classpath), mainClass);
     return runProcess(builder);
   }
 
diff --git a/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java b/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
index 5fb3007..d5642f8 100644
--- a/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
+++ b/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.utils.DexInspector.InstructionSubject;
 import com.android.tools.r8.utils.DexInspector.InvokeInstructionSubject;
 import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.StringUtils;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -43,7 +44,9 @@
     builder.setMainClass("rewrite.LongCompare");
     try {
       String output = ToolHelper.runArt(builder);
-      Assert.assertEquals("-1\n1\n-1\n0\ntrue\nfalse\nfalse\nfalse\n", output);
+      Assert
+          .assertEquals(StringUtils.lines("-1", "1", "-1", "0", "true", "false", "false", "false"),
+              output);
     } catch (IOException e) {
       Assert.fail();
     }
diff --git a/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java b/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
index b8d8e58..9e5a839 100644
--- a/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
@@ -7,6 +7,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.code.Instruction;
 import com.android.tools.r8.code.Sput;
 import com.android.tools.r8.code.SputObject;
@@ -26,6 +27,7 @@
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.MethodSubject;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
 import org.junit.Test;
 
 public class StaticValuesTest extends SmaliTestBase {
@@ -146,7 +148,7 @@
 
     String result = runArt(processedApplication, options);
 
-    assertEquals("true\n1\n2\n3\n4\n5.0\n6.0\n7\n8\n", result);
+    assertEquals(StringUtils.lines("true", "1", "2", "3", "4", "5.0", "6.0", "7", "8"), result);
   }
 
   @Test
@@ -185,7 +187,7 @@
 
     String result = runArt(processedApplication, options);
 
-    assertEquals("0\n1\n", result);
+    assertEquals(StringUtils.lines("0", "1"), result);
   }
 
   @Test
@@ -227,7 +229,7 @@
 
     String result = runArt(processedApplication, options);
 
-    assertEquals("null\nnull\nnull\n", result);
+    assertEquals(StringUtils.lines("null", "null", "null"), result);
   }
 
   @Test
@@ -270,7 +272,7 @@
 
     String result = runArt(processedApplication, options);
 
-    assertEquals("Value1\nValue2\nValue2\n", result);
+    assertEquals(StringUtils.lines("Value1", "Value2", "Value2"), result);
   }
 
   @Test
@@ -332,7 +334,7 @@
 
     String result = runArt(processedApplication, options);
 
-    assertEquals("3\n7\n", result);
+    assertEquals(StringUtils.lines("3", "7") , result);
   }
 
 
@@ -416,7 +418,7 @@
 
     String result = runArt(processedApplication, options);
 
-    assertEquals("3\n7\n", result);
+    assertEquals(StringUtils.lines("3", "7"), result);
   }
 
   @Test
@@ -484,8 +486,8 @@
 
     String result = runArt(processedApplication, options, className);
 
-    assertEquals(
-        "Test\n" + className + "\nTest\n" + className + "\nTest\n"  + className + "\n", result);
+    assertEquals(StringUtils.lines("Test", className, "Test", className, "Test", className),
+        result);
   }
 
   @Test
@@ -532,7 +534,7 @@
 
     String result = runArt(processedApplication, options, className);
 
-    assertEquals("Test2\norg.example.Test2\n", result);
+    assertEquals(StringUtils.lines("Test2", "org.example.Test2"), result);
   }
 
   @Test
@@ -567,7 +569,7 @@
 
     String result = runArt(processedApplication, options);
 
-    assertEquals("2\n", result);
+    assertEquals(StringUtils.lines("2"), result);
   }
 
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index a1d8e2a..b285ac0 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.utils.DexInspector.InstructionSubject;
 import com.android.tools.r8.utils.DexInspector.MethodSubject;
 import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
@@ -493,53 +494,59 @@
   }
 
   private static void assumenosideeffects1CheckOutput(String output1, String output2) {
-    Assert.assertEquals("noSideEffectVoid\nnoSideEffectInt\n", output1);
+    Assert.assertEquals(StringUtils.lines("noSideEffectVoid", "noSideEffectInt"), output1);
     Assert.assertEquals("", output2);
   }
 
   private static void assumenosideeffects2CheckOutput(String output1, String output2) {
-    Assert.assertEquals("Hello, world!\n", output1);
+    Assert.assertEquals(StringUtils.lines("Hello, world!"), output1);
     Assert.assertEquals("", output2);
   }
 
   private static void assumenosideeffects3CheckOutput(String output1, String output2) {
-    Assert.assertEquals("0\n1\n0L\n1L\n", output1);
-    Assert.assertEquals("1\n0\n1L\n0L\n", output2);
+    Assert.assertEquals(StringUtils.lines("0", "1", "0L", "1L"), output1);
+    Assert.assertEquals(StringUtils.lines("1", "0", "1L", "0L"), output2);
   }
 
   private static void assumenosideeffects4CheckOutput(String output1, String output2) {
-    Assert.assertEquals("method0\n0\nmethod1\n1\nmethod0L\n0L\nmethod1L\n1L\n", output1);
-    Assert.assertEquals("1\n0\n1L\n0L\n", output2);
+    Assert.assertEquals(
+        StringUtils.lines("method0", "0", "method1", "1", "method0L", "0L", "method1L", "1L"),
+        output1);
+    Assert.assertEquals(StringUtils.lines("1", "0", "1L", "0L"), output2);
   }
 
   private static void assumenosideeffects5CheckOutput(String output1, String output2) {
-    Assert.assertEquals("methodTrue\ntrue\nmethodFalse\nfalse\n", output1);
-    Assert.assertEquals("false\ntrue\n", output2);
+    Assert.assertEquals(StringUtils.lines("methodTrue", "true", "methodFalse", "false"), output1);
+    Assert.assertEquals(StringUtils.lines("false", "true"), output2);
   }
 
   private static void assumevalues1CheckOutput(String output1, String output2) {
-    Assert.assertEquals("3\n3L\n", output1);
-    Assert.assertEquals("1\n1L\n", output2);
+    Assert.assertEquals(StringUtils.lines("3", "3L"), output1);
+    Assert.assertEquals(StringUtils.lines("1", "1L"), output2);
   }
 
   private static void assumevalues2CheckOutput(String output1, String output2) {
-    Assert.assertEquals("1\n2\n3\n4\n1L\n2L\n3L\n4L\n", output1);
-    Assert.assertEquals("2\n3\n2L\n3L\n", output2);
+    Assert.assertEquals(StringUtils.lines("1", "2", "3", "4", "1L", "2L", "3L", "4L"), output1);
+    Assert.assertEquals(StringUtils.lines("2", "3", "2L", "3L"), output2);
   }
 
   private static void assumevalues3CheckOutput(String output1, String output2) {
-    Assert.assertEquals("3\n3L\n", output1);
-    Assert.assertEquals("1\n1L\n", output2);
+    Assert.assertEquals(StringUtils.lines("3", "3L"), output1);
+    Assert.assertEquals(StringUtils.lines("1", "1L"), output2);
   }
 
   private static void assumevalues4CheckOutput(String output1, String output2) {
-    Assert.assertEquals("method0\n0\nmethod1\n1\nmethod0L\n0L\nmethod1L\n1L\n", output1);
-    Assert.assertEquals("method0\n1\nmethod1\n0\nmethod0L\n1L\nmethod1L\n0L\n", output2);
+    Assert.assertEquals(
+        StringUtils.lines("method0", "0", "method1", "1", "method0L", "0L", "method1L", "1L"),
+        output1);
+    Assert.assertEquals(
+        StringUtils.lines("method0", "1", "method1", "0", "method0L", "1L", "method1L", "0L"),
+        output2);
   }
 
   private static void assumevalues5CheckOutput(String output1, String output2) {
-    Assert.assertEquals("methodTrue\ntrue\nmethodFalse\nfalse\n", output1);
-    Assert.assertEquals("methodTrue\nfalse\nmethodFalse\ntrue\n", output2);
+    Assert.assertEquals(StringUtils.lines("methodTrue", "true", "methodFalse", "false"), output1);
+    Assert.assertEquals(StringUtils.lines("methodTrue", "false", "methodFalse", "true"), output2);
   }
 
   private static void annotationRemovalHasAllInnerClassAnnotations(DexInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/smali/JumboStringTest.java b/src/test/java/com/android/tools/r8/smali/JumboStringTest.java
index 56c041c..8d4b841 100644
--- a/src/test/java/com/android/tools/r8/smali/JumboStringTest.java
+++ b/src/test/java/com/android/tools/r8/smali/JumboStringTest.java
@@ -6,6 +6,7 @@
 
 import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.StringUtils;
@@ -18,20 +19,23 @@
   public void test() throws Exception {
     StringBuilder builder = new StringBuilder();
     StringBuilder expectedBuilder = new StringBuilder();
-    builder.append("    new-instance         v0, Ljava/lang/StringBuilder;\n");
-    builder.append("    invoke-direct        { v0 }, Ljava/lang/StringBuilder;-><init>()V\n");
+    builder.append(StringUtils.lines("    new-instance         v0, Ljava/lang/StringBuilder;"));
+    builder.append(StringUtils.lines("    invoke-direct        { v0 }, Ljava/lang/StringBuilder;"
+        + "-><init>()V"));
     for (int i = 0; i <= 0xffff + 2; i++) {
       String prefixed = StringUtils.zeroPrefix(i, 5);
       expectedBuilder.append(prefixed);
-      expectedBuilder.append("\n");
-      builder.append("  const-string         v1, \"" + prefixed + "\\n\"\n");
+      expectedBuilder.append(StringUtils.lines(""));
+      builder.append(StringUtils.lines("  const-string         v1, \"" + prefixed + "\\n\""));
       builder.append(
-          "  invoke-virtual       { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n");
+          StringUtils.lines("  invoke-virtual       { v0, v1 }, Ljava/lang/StringBuilder;"
+              + "->append(Ljava/lang/String;)Ljava/lang/StringBuilder;"));
     }
     builder.append(
-        "    invoke-virtual       { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n");
-    builder.append("    move-result-object   v0\n");
-    builder.append("    return-object               v0\n");
+        StringUtils.lines("    invoke-virtual       { v0 }, Ljava/lang/StringBuilder;"
+            + "->toString()Ljava/lang/String;"));
+    builder.append(StringUtils.lines("    move-result-object   v0"));
+    builder.append(StringUtils.lines("    return-object               v0"));
 
     SmaliBuilder smaliBuilder = new SmaliBuilder(DEFAULT_CLASS_NAME);
 
diff --git a/src/test/java/com/android/tools/r8/smali/Regress38014736.java b/src/test/java/com/android/tools/r8/smali/Regress38014736.java
index c97d412..bf89f7f 100644
--- a/src/test/java/com/android/tools/r8/smali/Regress38014736.java
+++ b/src/test/java/com/android/tools/r8/smali/Regress38014736.java
@@ -5,8 +5,10 @@
 
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
 import org.junit.Test;
 
 public class Regress38014736 extends SmaliTestBase {
@@ -52,7 +54,7 @@
     String result = runArt(processedApplication, options);
     // The art runtime changed the way exceptions are printed. Therefore, we only check
     // for the type of the exception and that the message mentions null.
-    assertTrue(result.startsWith("0\njava.lang.NumberFormatException:"));
+    assertTrue(result.startsWith(StringUtils.joinLines("0", "java.lang.NumberFormatException:")));
     assertTrue(result.contains("null"));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/smali/RunArtSmokeTest.java b/src/test/java/com/android/tools/r8/smali/RunArtSmokeTest.java
index e1dc5c4..6a39c6d 100644
--- a/src/test/java/com/android/tools/r8/smali/RunArtSmokeTest.java
+++ b/src/test/java/com/android/tools/r8/smali/RunArtSmokeTest.java
@@ -7,6 +7,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.code.ConstString;
 import com.android.tools.r8.code.InvokeVirtual;
 import com.android.tools.r8.code.ReturnVoid;
@@ -15,6 +16,7 @@
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.Iterables;
 import org.junit.Test;
 
@@ -49,6 +51,6 @@
 
     // Run the generated code in Art.
     String result = runArt(processedApplication, options);
-    assertEquals("Hello, world!\n", result);
+    assertEquals(StringUtils.lines("Hello, world!"), result);
   }
 }