Added simple test to compile framework.jar with varying number of internal compilation treads. Added Marker for storing tools information in classes.dex.

Bug:
Change-Id: I53116cf3d796d176d0eae5dc42d92a475bc7c3f2
diff --git a/build.gradle b/build.gradle
index 60357de..e2c21b6 100644
--- a/build.gradle
+++ b/build.gradle
@@ -101,6 +101,7 @@
 
 dependencies {
     compile 'net.sf.jopt-simple:jopt-simple:4.6'
+    compile 'com.googlecode.json-simple:json-simple:1.1.1'
     compile group: 'com.google.guava', name: 'guava', version: '19.0'
     compile group: 'it.unimi.dsi', name: 'fastutil', version: '7.2.0'
     compile group: 'org.apache.commons', name: 'commons-compress', version: '1.12'
@@ -397,6 +398,20 @@
     }
 }
 
+task ExtractMarker(type: Jar) {
+    from sourceSets.main.output
+    baseName 'extractmarker'
+    manifest {
+      attributes 'Main-Class': 'com.android.tools.r8.ExtractMarker'
+    }
+    // In order to build without dependencies, pass the exclude_deps property using:
+    // gradle -Pexclude_deps ExtractMarker
+    if (!project.hasProperty('exclude_deps')) {
+        // Also include dependencies
+        from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
+    }
+}
+
 task sourceJar(type: Jar, dependsOn: classes) {
     classifier = 'src'
     from sourceSets.main.allSource
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index bca7a9f..a6da2ec 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -7,6 +7,8 @@
 
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.ApplicationWriter;
+import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.MainDexError;
 import com.android.tools.r8.graph.AppInfo;
@@ -152,6 +154,14 @@
     }
   }
 
+  // Compute the marker to be placed in the main dex file.
+  private static Marker getMarker(InternalOptions options) {
+    if (options.customizedMarker != null) {
+      return options.customizedMarker;
+    }
+    return new Marker(Tool.D8);
+  }
+
   private static CompilationResult runForTesting(
       AndroidApp inputApp, InternalOptions options, ExecutorService executor) throws IOException {
     try {
@@ -174,11 +184,11 @@
         options.methodsFilter.forEach((m) -> System.out.println("  - " + m));
         return null;
       }
-
+      Marker marker = getMarker(options);
       CompilationResult output =
           new CompilationResult(
               new ApplicationWriter(
-                  app, appInfo, options, null, NamingLens.getIdentityLens(), null)
+                  app, appInfo, options, marker, null, NamingLens.getIdentityLens(), null)
                   .write(null, executor),
               app,
               appInfo);
diff --git a/src/main/java/com/android/tools/r8/ExtractMarker.java b/src/main/java/com/android/tools/r8/ExtractMarker.java
new file mode 100644
index 0000000..c1234b8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ExtractMarker.java
@@ -0,0 +1,122 @@
+// Copyright (c) 2017, the Rex 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;
+
+import com.google.common.collect.ImmutableList;
+
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OutputMode;
+import com.android.tools.r8.utils.Timing;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+
+public class ExtractMarker {
+  private static class Command extends BaseCommand {
+
+    public static class Builder
+        extends BaseCommand.Builder<ExtractMarker.Command, ExtractMarker.Command.Builder> {
+
+      private Builder() {
+        super(CompilationMode.RELEASE);
+      }
+
+      @Override
+      ExtractMarker.Command.Builder self() {
+        return this;
+      }
+
+      @Override
+      public ExtractMarker.Command build() throws CompilationException, IOException {
+        // If printing versions ignore everything else.
+        if (isPrintHelp()) {
+          return new ExtractMarker.Command(isPrintHelp());
+        }
+        validate();
+        return new ExtractMarker.Command(
+            getAppBuilder().build(), getOutputPath(), getOutputMode(), getMode(), getMinApiLevel());
+      }
+    }
+
+    static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of(
+        "Usage: extractmarker [options] <input-files>",
+        " where <input-files> are dex files",
+        "  --version               # Print the version of r8.",
+        "  --help                  # Print this message."));
+
+    public static ExtractMarker.Command.Builder builder() {
+      return new ExtractMarker.Command.Builder();
+    }
+
+    public static ExtractMarker.Command.Builder parse(String[] args)
+        throws CompilationException, IOException {
+      ExtractMarker.Command.Builder builder = builder();
+      parse(args, builder);
+      return builder;
+    }
+
+    private static void parse(String[] args, ExtractMarker.Command.Builder builder)
+        throws CompilationException, IOException {
+      for (int i = 0; i < args.length; i++) {
+        String arg = args[i].trim();
+        if (arg.length() == 0) {
+          continue;
+        } else if (arg.equals("--help")) {
+          builder.setPrintHelp(true);
+        } else {
+          if (arg.startsWith("--")) {
+            throw new CompilationException("Unknown option: " + arg);
+          }
+          builder.addProgramFiles(Paths.get(arg));
+        }
+      }
+    }
+
+    private Command(
+        AndroidApp inputApp,
+        Path outputPath,
+        OutputMode outputMode,
+        CompilationMode mode,
+        int minApiLevel) {
+      super(inputApp, outputPath, outputMode, mode, minApiLevel);
+    }
+
+    private Command(boolean printHelp) {
+      super(printHelp, false);
+    }
+
+    @Override
+    InternalOptions getInternalOptions() {
+      return new InternalOptions();
+    }
+  }
+
+  public static void main(String[] args)
+      throws IOException, ProguardRuleParserException, CompilationException, ExecutionException {
+    ExtractMarker.Command.Builder builder = ExtractMarker.Command.parse(args);
+    ExtractMarker.Command command = builder.build();
+    if (command.isPrintHelp()) {
+      System.out.println(ExtractMarker.Command.USAGE_MESSAGE);
+      return;
+    }
+    AndroidApp app = command.getInputApp();
+    DexApplication dexApp =
+        new ApplicationReader(app, new InternalOptions(), new Timing("ExtractMarker")).read();
+    Marker readMarker = dexApp.dexItemFactory.extractMarker();
+    if (readMarker == null) {
+      System.out.println("D8/R8 marker not found.");
+      System.exit(1);
+    } else {
+      System.out.println(readMarker.toString());
+      System.exit(0);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 5f56d79..4393a3e 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -8,6 +8,8 @@
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.ApplicationWriter;
 import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.MainDexError;
 import com.android.tools.r8.graph.AppInfo;
@@ -77,6 +79,14 @@
     options.itemFactory.resetSortedIndices();
   }
 
+  // Compute the marker to be placed in the main dex file.
+  private static Marker getMarker(InternalOptions options) {
+    if (options.customizedMarker != null) {
+      return options.customizedMarker;
+    }
+    return new Marker(Tool.R8);
+  }
+
   public static AndroidApp writeApplication(
       ExecutorService executorService,
       DexApplication application,
@@ -88,8 +98,9 @@
       InternalOptions options)
       throws ExecutionException {
     try {
+      Marker marker = getMarker(options);
       return new ApplicationWriter(
-          application, appInfo, options, deadCode, namingLens, proguardSeedsData)
+          application, appInfo, options, marker, deadCode, namingLens, proguardSeedsData)
           .write(packageDistribution, executorService);
     } catch (IOException e) {
       throw new RuntimeException("Cannot write dex application", e);
diff --git a/src/main/java/com/android/tools/r8/bisect/Bisect.java b/src/main/java/com/android/tools/r8/bisect/Bisect.java
index 4fe3773..ee4477d 100644
--- a/src/main/java/com/android/tools/r8/bisect/Bisect.java
+++ b/src/main/java/com/android/tools/r8/bisect/Bisect.java
@@ -177,7 +177,7 @@
       throws IOException, ExecutionException {
     InternalOptions options = new InternalOptions();
     AppInfo appInfo = new AppInfo(app);
-    ApplicationWriter writer = new ApplicationWriter(app, appInfo, options, null, null, null);
+    ApplicationWriter writer = new ApplicationWriter(app, appInfo, options, null, null, null, null);
     AndroidApp outApp = writer.write(null, executor);
     outApp.writeToDirectory(output, OutputMode.Indexed);
   }
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 1cb0fb0..e6ec968 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.graph.DexDebugInfo;
 import com.android.tools.r8.graph.DexEncodedArray;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.DexValue;
@@ -43,6 +44,7 @@
   public final NamingLens namingLens;
   public final byte[] proguardSeedsData;
   public final InternalOptions options;
+  public DexString markerString;
 
   private static class SortAnnotations extends MixedSectionCollection {
 
@@ -105,6 +107,7 @@
       DexApplication application,
       AppInfo appInfo,
       InternalOptions options,
+      Marker marker,
       byte[] deadCode,
       NamingLens namingLens,
       byte[] proguardSeedsData) {
@@ -113,6 +116,9 @@
     this.appInfo = appInfo;
     assert options != null;
     this.options = options;
+    this.markerString = (marker == null)
+        ? null
+        : application.dexItemFactory.createString(marker.toString());
     this.deadCode = deadCode;
     this.namingLens = namingLens;
     this.proguardSeedsData = proguardSeedsData;
@@ -123,6 +129,8 @@
     application.timing.begin("DexApplication.write");
     try {
       application.dexItemFactory.sort(namingLens);
+      assert this.markerString == null || application.dexItemFactory.extractMarker() != null;
+
       SortAnnotations sortAnnotations = new SortAnnotations();
       application.classes().forEach((clazz) -> clazz.addDependencies(sortAnnotations));
 
@@ -148,7 +156,8 @@
         distributor =
             new VirtualFile.PackageMapDistributor(this, packageDistribution, executorService);
       } else {
-        distributor = new VirtualFile.FillFilesDistributor(this, options.minimalMainDex);
+        boolean minimal = options.minimalMainDex && !application.mainDexList.isEmpty();
+        distributor = new VirtualFile.FillFilesDistributor(this, minimal);
       }
       Map<Integer, VirtualFile> newFiles = distributor.run();
 
diff --git a/src/main/java/com/android/tools/r8/dex/Marker.java b/src/main/java/com/android/tools/r8/dex/Marker.java
new file mode 100644
index 0000000..4762fd4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/Marker.java
@@ -0,0 +1,115 @@
+// Copyright (c) 2017, the Rex 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.dex;
+
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+
+/**
+ * Abstraction for hidden dex marker intended for the main dex file.
+ */
+public class Marker {
+
+  public enum Tool {D8, R8}
+
+  private static final String kPrefix = "~~";
+  private static final String kD8prefix = kPrefix + Tool.D8 + "{";
+  private static final String kR8prefix = kPrefix + Tool.R8 + "{";
+
+  private final TreeMap<String, Object> content;
+  private final Tool tool;
+
+  public Marker(Tool tool) {
+    this.tool = tool;
+    this.content = new TreeMap<>();
+  }
+
+  private Marker(Tool tool, JSONObject object) {
+    this.tool = tool;
+    content = new TreeMap<>();
+    // This loop is necessary to make the type checker to shut up.
+    for (Object e : object.entrySet()) {
+      Map.Entry entry = (Map.Entry) e;
+      content.put(String.valueOf(entry.getKey()), entry.getValue());
+    }
+  }
+
+  public Marker put(String key, int value) {
+    // value is converted to Long ensuring equals works with the parsed json string.
+    return internalPut(key, new Long(value));
+  }
+
+  public Marker put(String key, String value) {
+    return internalPut(key, value);
+  }
+
+  private Marker internalPut(String key, Object value) {
+    assert (key != null) && (value != null);
+    assert !content.containsKey(key);
+    content.put(key, value);
+    return this;
+  }
+
+  @Override
+  public String toString() {
+    // The JSONObject does not support a predictable sorted serialization of the object.
+    // Therefore, a TreeMap is used and iteration is over the keySet.
+    StringBuffer sb = new StringBuffer(kPrefix + tool);
+    boolean first = true;
+    sb.append('{');
+    for (String key : content.keySet()) {
+      if (first) {
+        first = false;
+      } else {
+        sb.append(',');
+      }
+      sb.append(JSONObject.toString(key, content.get(key)));
+    }
+    sb.append('}');
+    return sb.toString();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof Marker) {
+      Marker other = (Marker) obj;
+      return (tool == other.tool) && content.equals(other.content);
+    }
+    return false;
+  }
+
+  @Override
+  public int hashCode() {
+    return tool.hashCode() + 3 * content.hashCode();
+  }
+
+  // Try to parse str as a marker.
+  // Returns null if parsing fails.
+  public static Marker parse(String str) {
+    if (str.startsWith(kD8prefix)) {
+      return internalParse(Tool.D8, str.substring(kD8prefix.length() - 1));
+    }
+    if (str.startsWith(kR8prefix)) {
+      return internalParse(Tool.R8, str.substring(kR8prefix.length() - 1));
+    }
+    return null;
+  }
+
+  private static Marker internalParse(Tool tool, String str) {
+    try {
+      Object result =  new JSONParser().parse(str);
+      if (result instanceof JSONObject) {
+        return new Marker(tool, (JSONObject) result);
+      }
+    } catch (ParseException e) {
+      // Fall through.
+    }
+    return null;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 3432b62..f4fa536 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -318,7 +318,12 @@
       // 1. Place the remaining files based on their packages in sorted order.
 
       // Start with 1 file. The package populator will add more if needed.
-      nameToFileMap.put(0, new VirtualFile(0, writer.namingLens));
+      VirtualFile main = new VirtualFile(0, writer.namingLens);
+      nameToFileMap.put(0, main);
+      if (writer.markerString != null) {
+        main.transaction.addString(writer.markerString);
+        main.commitTransaction();
+      }
 
       // First fill required classes into the main dex file.
       fillForMainDexList(classes);
@@ -925,7 +930,7 @@
         } else {
           assert clazz.superType != null;
           // We don't have a package, add this to a list of classes that we will add last.
-          assert current.transaction.isEmpty();
+          assert current.transaction.classes.isEmpty();
           nonPackageClasses.add(clazz);
           continue;
         }
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 3feae5f..a107794 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.graph.DexDebugEvent.AdvanceLine;
 import com.android.tools.r8.graph.DexDebugEvent.AdvancePC;
 import com.android.tools.r8.graph.DexDebugEvent.Default;
@@ -287,6 +288,18 @@
     return canonicalize(strings, new DexString(source));
   }
 
+  // Debugging support to extract marking string.
+  synchronized public Marker extractMarker() {
+    // This is slow but it is not needed for any production code yet.
+    for (DexString dexString : strings.keySet()) {
+      Marker result = Marker.parse(dexString.toString());
+      if (result != null) {
+        return result;
+      }
+    }
+    return null;
+  }
+
   public DexType createType(DexString descriptor) {
     assert !sorted;
     DexType type = new DexType(descriptor);
diff --git a/src/main/java/com/android/tools/r8/graph/DexString.java b/src/main/java/com/android/tools/r8/graph/DexString.java
index e6914ec..75a6d35 100644
--- a/src/main/java/com/android/tools/r8/graph/DexString.java
+++ b/src/main/java/com/android/tools/r8/graph/DexString.java
@@ -21,7 +21,7 @@
     this.content = content;
   }
 
-  DexString(String string) {
+  public DexString(String string) {
     this.size = string.length();
     this.content = encode(string);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
index 74a0800..c8157d6 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
@@ -90,8 +90,7 @@
     for (IndexedDexItem item : items) {
       item.assignVirtualFileIndex(virtualFileId, index);
       // For strings collect the first jumbo string (if any).
-      if (index > Constants.MAX_NON_JUMBO_INDEX) {
-        assert item instanceof DexString;
+      if ((index > Constants.MAX_NON_JUMBO_INDEX) && (item instanceof DexString)) {
         if (index == Constants.FIRST_JUMBO_INDEX) {
           firstJumboString = (DexString) item;
         }
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 d43dc36..6f94b1e 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
@@ -50,7 +50,7 @@
 
 public class IRConverter {
 
-  public static final int PEEPHOLE_OPTIMIZATION_PASSES = 2;
+  private static final int PEEPHOLE_OPTIMIZATION_PASSES = 2;
 
   private final Timing timing;
   public final DexApplication application;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index f547209..ce6ad76 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -212,15 +212,15 @@
 
     public final DexEncodedMethod target;
     public final Invoke invoke;
-    public final Reason reason;
+    final Reason reason;
 
-    public InlineAction(DexEncodedMethod target, Invoke invoke, Reason reason) {
+    InlineAction(DexEncodedMethod target, Invoke invoke, Reason reason) {
       this.target = target;
       this.invoke = invoke;
       this.reason = reason;
     }
 
-    public boolean forceInline() {
+    boolean forceInline() {
       return reason != Reason.SIMPLE;
     }
 
@@ -313,16 +313,6 @@
     return true;
   }
 
-  /// Computer the receiver value for the holder method.
-  private Value receiverValue(DexEncodedMethod method, IRCode code) {
-    // Ignore static methods.
-    if (method.accessFlags.isStatic()) {
-      return null;
-    }
-    // Find the outValue of the first argument instruction in the first block.
-    return code.collectArguments().get(0);
-  }
-
   public void performInlining(DexEncodedMethod method, IRCode code, CallGraph callGraph) {
     int instruction_allowance = 1500;
     instruction_allowance -= numberOfInstructions(code);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
index fb6c2eb..d32f7f4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
@@ -29,7 +29,7 @@
   private final CallGraph callGraph;
   private final InliningInfo info;
 
-  public InliningOracle(
+  InliningOracle(
       Inliner inliner,
       DexEncodedMethod method,
       CallGraph callGraph) {
@@ -39,7 +39,7 @@
     info = Log.ENABLED ? new InliningInfo(method) : null;
   }
 
-  public void finish() {
+  void finish() {
     if (Log.ENABLED) {
       System.out.println(info.toString());
     }
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 ca22e50..9fea848 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.utils;
 
 import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -45,6 +46,9 @@
   // Silencing output.
   public boolean quiet = false;
 
+  // Hidden marker for classes.dex
+  public Marker customizedMarker;
+
   public List<String> methodsFilter = ImmutableList.of();
   public int minApiLevel = Constants.DEFAULT_ANDROID_API;
   // Skipping min_api check and compiling an intermediate result intended for later merging.
diff --git a/src/test/java/com/android/tools/r8/d8/D8FrameworkTest.java b/src/test/java/com/android/tools/r8/d8/D8FrameworkTest.java
new file mode 100644
index 0000000..2878ed0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/d8/D8FrameworkTest.java
@@ -0,0 +1,79 @@
+// Copyright (c) 2017, the Rex 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.d8;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Simple test that compiles framework.jar with D8 a number of times with
+ * various number of threads available to the compiler.
+ * This test also tests the hidden marker inserted into classes.dex.
+ */
+@RunWith( Parameterized.class )
+public class D8FrameworkTest {
+
+  private static final Path FRAMEWORK_JAR =
+      Paths.get("tools/linux/art-5.1.1/product/mako/system/framework/framework.jar");
+
+  @Rule
+  public TemporaryFolder output = ToolHelper.getTemporaryFolderForTest();
+
+  @Parameters(name = "Number of threads = {0}")
+  public static Collection<Object[]> data() {
+    return Arrays.asList(new Object[][] { {1}, {2}, {4}, {8}, {16} });
+  }
+
+  private final int threads;
+
+  public D8FrameworkTest(int threads) {
+    this.threads = threads;
+  }
+
+  @Test
+  public void compile() throws CompilationException, IOException, ExecutionException {
+    D8Command command = D8Command.builder()
+        .setMinApiLevel(Constants.ANDROID_N_API)
+        .addProgramFiles(FRAMEWORK_JAR)
+        .build();
+    Marker marker = new Marker(Tool.D8)
+        .put("revision", "1.0.0")
+        .put("threads", threads);
+    Marker selfie = Marker.parse(marker.toString());
+    assert marker.equals(selfie);
+    AndroidApp app = ToolHelper.runD8(command, options -> {
+      options.customizedMarker = marker;
+      options.numberOfThreads = threads;
+    });
+    DexApplication dexApp =
+        new ApplicationReader(app, new InternalOptions(), new Timing("D8FrameworkTest")).read();
+    Marker readMarker = dexApp.dexItemFactory.extractMarker();
+    assertEquals(marker, readMarker);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index a113003..a87985e 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -581,7 +581,7 @@
     DexApplication application = builder.build();
     AppInfoWithSubtyping appInfo = new AppInfoWithSubtyping(application);
     ApplicationWriter writer = new ApplicationWriter(
-        application, appInfo, options, null, NamingLens.getIdentityLens(), null);
+        application, appInfo, options, null, null, NamingLens.getIdentityLens(), null);
     ExecutorService executor = ThreadUtils.getExecutorService(options);
     try {
       return writer.write(null, executor);