Extend compile dump utils to startup profiles

Change-Id: I609c75a7445e674cc76737768cefbcd1fc0cbd15
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupProfileProviderUtils.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfileProviderUtils.java
index ae95407..3aea957 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupProfileProviderUtils.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfileProviderUtils.java
@@ -9,18 +9,68 @@
 import com.android.tools.r8.experimental.startup.profile.art.ARTProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.startup.StartupProfileBuilder;
 import com.android.tools.r8.startup.StartupProfileProvider;
 import com.android.tools.r8.startup.diagnostic.MissingStartupProfileItemsDiagnostic;
 import com.android.tools.r8.utils.ConsumerUtils;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.MethodReferenceUtils;
 import com.android.tools.r8.utils.UTF8TextInputStream;
+import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.UncheckedIOException;
+import java.nio.file.Files;
 import java.nio.file.Path;
 
 public class StartupProfileProviderUtils {
 
+  public static StartupProfileProvider createFromDumpFile(Path path) {
+    return new StartupProfileProvider() {
+
+      @Override
+      public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+        try {
+          try (BufferedReader bufferedReader = Files.newBufferedReader(path)) {
+            while (bufferedReader.ready()) {
+              String rule = bufferedReader.readLine();
+              if (rule.charAt(0) == 'S') {
+                String classDescriptor = rule.substring(1);
+                assert DescriptorUtils.isClassDescriptor(classDescriptor);
+                startupProfileBuilder.addSyntheticStartupMethod(
+                    syntheticStartupMethodBuilder ->
+                        syntheticStartupMethodBuilder.setSyntheticContextReference(
+                            Reference.classFromDescriptor(classDescriptor)));
+              } else {
+                MethodReference methodReference = MethodReferenceUtils.parseSmaliString(rule);
+                if (methodReference != null) {
+                  startupProfileBuilder.addStartupMethod(
+                      startupMethodBuilder ->
+                          startupMethodBuilder.setMethodReference(methodReference));
+                } else {
+                  assert DescriptorUtils.isClassDescriptor(rule);
+                  startupProfileBuilder.addStartupClass(
+                      startupClassBuilder ->
+                          startupClassBuilder.setClassReference(
+                              Reference.classFromDescriptor(rule)));
+                }
+              }
+            }
+          }
+        } catch (IOException e) {
+          throw new UncheckedIOException(e);
+        }
+      }
+
+      @Override
+      public Origin getOrigin() {
+        return new PathOrigin(path);
+      }
+    };
+  }
+
   public static StartupProfileProvider createFromHumanReadableARTProfile(Path path) {
     return new StartupProfileProvider() {
 
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/art/HumanReadableARTProfileParser.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/art/HumanReadableARTProfileParser.java
index 29ffaa6..e86501f 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/profile/art/HumanReadableARTProfileParser.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/art/HumanReadableARTProfileParser.java
@@ -8,18 +8,15 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.MethodReference;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.references.TypeReference;
 import com.android.tools.r8.startup.diagnostic.HumanReadableARTProfileParserErrorDiagnostic;
 import com.android.tools.r8.utils.Action;
-import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.ClassReferenceUtils;
+import com.android.tools.r8.utils.MethodReferenceUtils;
 import com.android.tools.r8.utils.Reporter;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.UncheckedIOException;
-import java.util.ArrayList;
-import java.util.List;
 
 public class HumanReadableARTProfileParser {
 
@@ -94,7 +91,7 @@
   }
 
   private boolean parseClassRule(String descriptor) {
-    ClassReference classReference = parseClassDescriptor(descriptor);
+    ClassReference classReference = ClassReferenceUtils.parseClassDescriptor(descriptor);
     if (classReference == null) {
       return false;
     }
@@ -104,7 +101,8 @@
 
   private boolean parseMethodRule(
       String descriptor, ARTProfileMethodRuleInfoImpl methodRuleInfo, int arrowStartIndex) {
-    MethodReference methodReference = parseMethodDescriptor(descriptor, arrowStartIndex);
+    MethodReference methodReference =
+        MethodReferenceUtils.parseSmaliString(descriptor, arrowStartIndex);
     if (methodReference == null) {
       return false;
     }
@@ -112,46 +110,6 @@
     return true;
   }
 
-  private ClassReference parseClassDescriptor(String classDescriptor) {
-    if (DescriptorUtils.isClassDescriptor(classDescriptor)) {
-      return Reference.classFromDescriptor(classDescriptor);
-    } else {
-      return null;
-    }
-  }
-
-  private MethodReference parseMethodDescriptor(
-      String startupMethodDescriptor, int arrowStartIndex) {
-    String classDescriptor = startupMethodDescriptor.substring(0, arrowStartIndex);
-    ClassReference methodHolder = parseClassDescriptor(classDescriptor);
-    if (methodHolder == null) {
-      return null;
-    }
-
-    int methodNameStartIndex = arrowStartIndex + 2;
-    String protoWithNameDescriptor = startupMethodDescriptor.substring(methodNameStartIndex);
-    int methodNameEndIndex = protoWithNameDescriptor.indexOf('(');
-    if (methodNameEndIndex <= 0) {
-      return null;
-    }
-    String methodName = protoWithNameDescriptor.substring(0, methodNameEndIndex);
-
-    String protoDescriptor = protoWithNameDescriptor.substring(methodNameEndIndex);
-    return parseMethodProto(methodHolder, methodName, protoDescriptor);
-  }
-
-  private MethodReference parseMethodProto(
-      ClassReference methodHolder, String methodName, String protoDescriptor) {
-    List<TypeReference> parameterTypes = new ArrayList<>();
-    for (String parameterTypeDescriptor :
-        DescriptorUtils.getArgumentTypeDescriptors(protoDescriptor)) {
-      parameterTypes.add(Reference.typeFromDescriptor(parameterTypeDescriptor));
-    }
-    String returnTypeDescriptor = DescriptorUtils.getReturnTypeDescriptor(protoDescriptor);
-    TypeReference returnType = Reference.returnTypeFromDescriptor(returnTypeDescriptor);
-    return Reference.method(methodHolder, methodName, parameterTypes, returnType);
-  }
-
   public static class Builder {
 
     private ARTProfileBuilder profileBuilder;
diff --git a/src/main/java/com/android/tools/r8/utils/ClassReferenceUtils.java b/src/main/java/com/android/tools/r8/utils/ClassReferenceUtils.java
index 30754f0..072022d 100644
--- a/src/main/java/com/android/tools/r8/utils/ClassReferenceUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ClassReferenceUtils.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
 import java.util.Comparator;
 
 public class ClassReferenceUtils {
@@ -36,6 +37,14 @@
     return COMPARATOR;
   }
 
+  public static ClassReference parseClassDescriptor(String classDescriptor) {
+    if (DescriptorUtils.isClassDescriptor(classDescriptor)) {
+      return Reference.classFromDescriptor(classDescriptor);
+    } else {
+      return null;
+    }
+  }
+
   public static DexType toDexType(ClassReference classReference, DexItemFactory dexItemFactory) {
     return dexItemFactory.createType(classReference.getDescriptor());
   }
diff --git a/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java b/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
index 3c2605a..82f1ce4 100644
--- a/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
+++ b/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.R8;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.R8Command.Builder;
+import com.android.tools.r8.experimental.startup.StartupProfileProviderUtils;
 import java.io.IOException;
 import java.lang.reflect.Method;
 import java.nio.file.Files;
@@ -24,6 +25,7 @@
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.function.Consumer;
+import java.util.stream.Collectors;
 
 /**
  * Wrapper to make it easy to call R8 in compat mode when compiling a dump file.
@@ -58,7 +60,8 @@
           "--pg-conf",
           "--pg-map-output",
           "--desugared-lib",
-          "--threads");
+          "--threads",
+          "--startup-profile");
 
   private static final List<String> VALID_OPTIONS_WITH_TWO_OPERANDS =
       Arrays.asList("--feature-jar");
@@ -84,6 +87,7 @@
     List<Path> classpath = new ArrayList<>();
     List<Path> config = new ArrayList<>();
     List<Path> mainDexRulesFiles = new ArrayList<>();
+    List<Path> startupProfileFiles = new ArrayList<>();
     int minApi = 1;
     int threads = -1;
     boolean enableMissingLibraryApiModeling = false;
@@ -169,6 +173,11 @@
               mainDexRulesFiles.add(Paths.get(operand));
               break;
             }
+          case "--startup-profile":
+            {
+              startupProfileFiles.add(Paths.get(operand));
+              break;
+            }
           default:
             throw new IllegalArgumentException("Unimplemented option: " + option);
         }
@@ -201,6 +210,10 @@
             .addClasspathFiles(classpath)
             .addProguardConfigurationFiles(config)
             .addMainDexRulesFiles(mainDexRulesFiles)
+            .addStartupProfileProviders(
+                startupProfileFiles.stream()
+                    .map(StartupProfileProviderUtils::createFromDumpFile)
+                    .collect(Collectors.toList()))
             .setOutput(outputPath, outputMode)
             .setMode(compilationMode);
     getReflectiveBuilderMethod(
diff --git a/src/main/java/com/android/tools/r8/utils/CompileDumpD8.java b/src/main/java/com/android/tools/r8/utils/CompileDumpD8.java
index b2c4269..f1007f6 100644
--- a/src/main/java/com/android/tools/r8/utils/CompileDumpD8.java
+++ b/src/main/java/com/android/tools/r8/utils/CompileDumpD8.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.experimental.startup.StartupProfileProviderUtils;
 import java.io.IOException;
 import java.lang.reflect.Method;
 import java.nio.file.Files;
@@ -19,6 +20,7 @@
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.function.Consumer;
+import java.util.stream.Collectors;
 
 /**
  * Wrapper to make it easy to call D8 mode when compiling a dump file.
@@ -50,7 +52,8 @@
           "--main-dex-list",
           "--main-dex-list-output",
           "--desugared-lib",
-          "--threads");
+          "--threads",
+          "--startup-profile");
 
   public static void main(String[] args) throws CompilationFailedException {
     OutputMode outputMode = OutputMode.DexIndexed;
@@ -61,6 +64,7 @@
     List<Path> library = new ArrayList<>();
     List<Path> classpath = new ArrayList<>();
     List<Path> mainDexRulesFiles = new ArrayList<>();
+    List<Path> startupProfileFiles = new ArrayList<>();
     int minApi = 1;
     int threads = -1;
     boolean enableMissingLibraryApiModeling = false;
@@ -131,6 +135,11 @@
               mainDexRulesFiles.add(Paths.get(operand));
               break;
             }
+          case "--startup-profile":
+            {
+              startupProfileFiles.add(Paths.get(operand));
+              break;
+            }
           default:
             throw new IllegalArgumentException("Unimplemented option: " + option);
         }
@@ -144,6 +153,10 @@
             .addLibraryFiles(library)
             .addClasspathFiles(classpath)
             .addMainDexRulesFiles(mainDexRulesFiles)
+            .addStartupProfileProviders(
+                startupProfileFiles.stream()
+                    .map(StartupProfileProviderUtils::createFromDumpFile)
+                    .collect(Collectors.toList()))
             .setOutput(outputPath, outputMode)
             .setMode(compilationMode);
     getReflectiveBuilderMethod(
diff --git a/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java b/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
index 4e1a4bc..251edcc 100644
--- a/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
@@ -16,9 +16,11 @@
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.references.TypeReference;
 import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Iterator;
+import java.util.List;
 
 public class MethodReferenceUtils {
 
@@ -106,6 +108,46 @@
     }
   }
 
+  public static MethodReference parseSmaliString(String classAndMethodDescriptor) {
+    int arrowStartIndex = classAndMethodDescriptor.indexOf("->");
+    if (arrowStartIndex >= 0) {
+      return parseSmaliString(classAndMethodDescriptor, arrowStartIndex);
+    }
+    return null;
+  }
+
+  public static MethodReference parseSmaliString(
+      String classAndMethodDescriptor, int arrowStartIndex) {
+    String classDescriptor = classAndMethodDescriptor.substring(0, arrowStartIndex);
+    ClassReference methodHolder = ClassReferenceUtils.parseClassDescriptor(classDescriptor);
+    if (methodHolder == null) {
+      return null;
+    }
+
+    int methodNameStartIndex = arrowStartIndex + 2;
+    String protoWithNameDescriptor = classAndMethodDescriptor.substring(methodNameStartIndex);
+    int methodNameEndIndex = protoWithNameDescriptor.indexOf('(');
+    if (methodNameEndIndex <= 0) {
+      return null;
+    }
+    String methodName = protoWithNameDescriptor.substring(0, methodNameEndIndex);
+
+    String protoDescriptor = protoWithNameDescriptor.substring(methodNameEndIndex);
+    return parseMethodProto(methodHolder, methodName, protoDescriptor);
+  }
+
+  private static MethodReference parseMethodProto(
+      ClassReference methodHolder, String methodName, String protoDescriptor) {
+    List<TypeReference> parameterTypes = new ArrayList<>();
+    for (String parameterTypeDescriptor :
+        DescriptorUtils.getArgumentTypeDescriptors(protoDescriptor)) {
+      parameterTypes.add(Reference.typeFromDescriptor(parameterTypeDescriptor));
+    }
+    String returnTypeDescriptor = DescriptorUtils.getReturnTypeDescriptor(protoDescriptor);
+    TypeReference returnType = Reference.returnTypeFromDescriptor(returnTypeDescriptor);
+    return Reference.method(methodHolder, methodName, parameterTypes, returnType);
+  }
+
   public static DexMethod toDexMethod(
       MethodReference methodReference, DexItemFactory dexItemFactory) {
     return dexItemFactory.createMethod(
diff --git a/tools/compiledump.py b/tools/compiledump.py
index 6baaada..8149313 100755
--- a/tools/compiledump.py
+++ b/tools/compiledump.py
@@ -189,12 +189,21 @@
       print("Unimplemented: proguard_input configuration.")
 
   def main_dex_list_resource(self):
-    if self.if_exists('main-dex-list.txt'):
-      print("Unimplemented: main-dex-list.")
+    return self.if_exists('main-dex-list.txt')
 
   def main_dex_rules_resource(self):
     return self.if_exists('main-dex-rules.txt')
 
+  def startup_profile_resources(self):
+    startup_profile_resources = []
+    while True:
+      current_startup_profile_index = len(startup_profile_resources) + 1
+      startup_profile_resource = self.if_exists(
+          'startup-profile-%s.txt' % current_startup_profile_index)
+      if startup_profile_resource is None:
+        return startup_profile_resources
+      startup_profile_resources.append(startup_profile_resource)
+
   def build_properties_file(self):
     return self.if_exists('build.properties')
 
@@ -470,8 +479,12 @@
         # -print{mapping,usage}
         clean_config(dump.config_file(), args)
       cmd.extend(['--pg-conf', dump.config_file()])
+    if dump.main_dex_list_resource():
+      cmd.extend(['--main-dex-list', dump.main_dex_list_resource()])
     if dump.main_dex_rules_resource():
       cmd.extend(['--main-dex-rules', dump.main_dex_rules_resource()])
+    for startup_profile_resource in dump.startup_profile_resources():
+      cmd.extend(['--startup-profile', startup_profile_resource])
     if compiler == 'l8':
       if dump.config_file():
         cmd.extend(['--pg-map-output', '%s.map' % out])