Merge "Avoid calling toString on all dex strings to collect markers."
diff --git a/src/main/java/com/android/tools/r8/dex/Marker.java b/src/main/java/com/android/tools/r8/dex/Marker.java
index c9af221..31628ad 100644
--- a/src/main/java/com/android/tools/r8/dex/Marker.java
+++ b/src/main/java/com/android/tools/r8/dex/Marker.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.dex;
 
+import com.android.tools.r8.graph.DexString;
 import java.util.Map;
 import java.util.TreeMap;
 import org.json.simple.JSONObject;
@@ -20,9 +21,10 @@
 
   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 static final char PREFIX_CHAR = '~';
+  private static final String PREFIX = "~~";
+  private static final String D8_PREFIX = PREFIX + Tool.D8 + "{";
+  private static final String R8_PREFIX = PREFIX + Tool.R8 + "{";
 
   private final TreeMap<String, Object> content;
   private final Tool tool;
@@ -92,7 +94,7 @@
   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);
+    StringBuffer sb = new StringBuffer(PREFIX + tool);
     boolean first = true;
     sb.append('{');
     for (String key : content.keySet()) {
@@ -123,12 +125,17 @@
 
   // 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));
+  public static Marker parse(DexString dexString) {
+    if (dexString.size > 2
+        && dexString.content[0] == PREFIX_CHAR
+        && dexString.content[1] == PREFIX_CHAR) {
+      String str = dexString.toString();
+      if (str.startsWith(D8_PREFIX)) {
+        return internalParse(Tool.D8, str.substring(D8_PREFIX.length() - 1));
+      }
+      if (str.startsWith(R8_PREFIX)) {
+        return internalParse(Tool.R8, str.substring(R8_PREFIX.length() - 1));
+      }
     }
     return null;
   }
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 d45402d..9e77dd5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -510,7 +510,7 @@
   public synchronized 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());
+      Marker result = Marker.parse(dexString);
       if (result != null) {
         return result;
       }
@@ -524,7 +524,7 @@
     // This is slow but it is not needed for any production code yet.
     List<Marker> markers = new ArrayList<>();
     for (DexString dexString : strings.keySet()) {
-      Marker marker = Marker.parse(dexString.toString());
+      Marker marker = Marker.parse(dexString);
       if (marker != null) {
         markers.add(marker);
       }
diff --git a/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java b/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java
index 9ce3d9c..7daaddd 100644
--- a/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java
@@ -11,6 +11,7 @@
 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.graph.DexString;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
@@ -61,12 +62,14 @@
     Marker marker = new Marker(Tool.D8)
         .setVersion("1.0.0")
         .setMinApi(minApi);
-    Marker selfie = Marker.parse(marker.toString());
+    InternalOptions options = new InternalOptions();
+    DexString markerDexString = options.itemFactory.createString(marker.toString());
+    Marker selfie = Marker.parse(markerDexString);
     assert marker.equals(selfie);
-    AndroidApp app = ToolHelper.runD8(command, options -> options.setMarker(marker));
+    AndroidApp app = ToolHelper.runD8(command, opts -> opts.setMarker(marker));
     DexApplication dexApp =
         new ApplicationReader(
-                app, new InternalOptions(), new Timing("D8FrameworkDexPassthroughMarkerTest"))
+                app, options, new Timing("D8FrameworkDexPassthroughMarkerTest"))
             .read();
     Marker readMarker = dexApp.dexItemFactory.extractMarker();
     assertEquals(marker, readMarker);