Support machine specifications as input

Change-Id: I77bafc9e72ee3e35b5a8c62d48ce23e71604e020
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecificationParser.java
index 83ee3ec..bc61d60 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecificationParser.java
@@ -8,7 +8,9 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecificationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecificationParser;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.synthesis.SyntheticNaming;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
@@ -20,6 +22,7 @@
 
   public static final String CONFIGURATION_FORMAT_VERSION_KEY = "configuration_format_version";
   private static final int MIN_HUMAN_CONFIGURATION_FORMAT_VERSION = 100;
+  private static final int MIN_MACHINE_CONFIGURATION_FORMAT_VERSION = 200;
 
   public static DesugaredLibrarySpecification parseDesugaredLibrarySpecification(
       StringResource stringResource,
@@ -27,26 +30,8 @@
       Reporter reporter,
       boolean libraryCompilation,
       int minAPILevel) {
-    Origin origin = stringResource.getOrigin();
-    assert origin != null;
-    String jsonConfigString;
-    JsonObject jsonConfig;
-    try {
-      jsonConfigString = stringResource.getString();
-      JsonParser parser = new JsonParser();
-      jsonConfig = parser.parse(jsonConfigString).getAsJsonObject();
-    } catch (Exception e) {
-      throw reporter.fatalError(new ExceptionDiagnostic(e, origin));
-    }
-
-    if (isHumanSpecification(jsonConfig, reporter, origin)) {
-      return new HumanDesugaredLibrarySpecificationParser(
-              dexItemFactory, reporter, libraryCompilation, minAPILevel)
-          .parse(origin, jsonConfigString, jsonConfig);
-    }
-    return new LegacyDesugaredLibrarySpecificationParser(
-            dexItemFactory, reporter, libraryCompilation, minAPILevel)
-        .parse(origin, jsonConfigString, jsonConfig);
+    return parseDesugaredLibrarySpecificationforTesting(
+        stringResource, dexItemFactory, reporter, libraryCompilation, minAPILevel, flags -> {});
   }
 
   public static DesugaredLibrarySpecification parseDesugaredLibrarySpecificationforTesting(
@@ -67,18 +52,53 @@
     } catch (Exception e) {
       throw reporter.fatalError(new ExceptionDiagnostic(e, origin));
     }
+    // Machine Specification is the shippable format released in Maven. D8/R8 has to be *very*
+    // backward compatible to any machine specification, and raise proper error messages for
+    // compatibility issues. The format is also exhaustive (Very limited pattern matching, if any).
+    // It can hardly be written by hand and is always generated.
+    if (isMachineSpecification(jsonConfig, reporter, origin)) {
+      return new MachineDesugaredLibrarySpecificationParser(
+              dexItemFactory, reporter, libraryCompilation, minAPILevel, new SyntheticNaming())
+          .parse(origin, jsonConfigString, jsonConfig);
+    }
+    // Human Specification is the easy to write format for developers and allows one to widely use
+    // pattern matching. This format is mainly used for development and to generate the machine
+    // specification. D8/R8 is *not* backward compatible with any previous version of human
+    // specification, which is therefore not suited to be shipped for external users. It can be
+    // shipped to internal users where we can easily update the D8/R8 compiler and the
+    // desugared library specification at the same time.
     if (isHumanSpecification(jsonConfig, reporter, origin)) {
       return new HumanDesugaredLibrarySpecificationParser(
               dexItemFactory, reporter, libraryCompilation, minAPILevel)
           .parse(origin, jsonConfigString, jsonConfig, topLevelFlagsAmender);
     }
+    // Legacy specification is the legacy format, as was shipped desugared library JDK8.
+    // Hopefully the day will come where this format is no longer supported, and the other formats
+    // shall always be preferred+.
     return new LegacyDesugaredLibrarySpecificationParser(
             dexItemFactory, reporter, libraryCompilation, minAPILevel)
         .parse(origin, jsonConfigString, jsonConfig, topLevelFlagsAmender);
   }
 
+  public static boolean isMachineSpecification(
+      JsonObject jsonConfig, Reporter reporter, Origin origin) {
+    ensureConfigurationFormatVersion(jsonConfig, reporter, origin);
+
+    int formatVersion = jsonConfig.get(CONFIGURATION_FORMAT_VERSION_KEY).getAsInt();
+    return formatVersion >= MIN_MACHINE_CONFIGURATION_FORMAT_VERSION;
+  }
+
   public static boolean isHumanSpecification(
       JsonObject jsonConfig, Reporter reporter, Origin origin) {
+    ensureConfigurationFormatVersion(jsonConfig, reporter, origin);
+
+    int formatVersion = jsonConfig.get(CONFIGURATION_FORMAT_VERSION_KEY).getAsInt();
+    return formatVersion >= MIN_HUMAN_CONFIGURATION_FORMAT_VERSION
+        && formatVersion < MIN_MACHINE_CONFIGURATION_FORMAT_VERSION;
+  }
+
+  private static void ensureConfigurationFormatVersion(
+      JsonObject jsonConfig, Reporter reporter, Origin origin) {
     if (!jsonConfig.has(CONFIGURATION_FORMAT_VERSION_KEY)) {
       throw reporter.fatalError(
           new StringDiagnostic(
@@ -87,8 +107,5 @@
                   + "'",
               origin));
     }
-
-    return jsonConfig.get(CONFIGURATION_FORMAT_VERSION_KEY).getAsInt()
-        >= MIN_HUMAN_CONFIGURATION_FORMAT_VERSION;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
index 0d9ef85..f4a6d2a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
@@ -56,10 +56,12 @@
     this.rewritingFlags = rewritingFlags;
   }
 
+  @Override
   public boolean isEmpty() {
     return rewritingFlags.isEmpty();
   }
 
+  @Override
   public boolean isLibraryCompilation() {
     return libraryCompilation;
   }
@@ -72,10 +74,12 @@
     return rewritingFlags;
   }
 
-  public AndroidApiLevel getRequiredCompilationAPILevel() {
-    return topLevelFlags.getRequiredCompilationAPILevel();
+  @Override
+  public AndroidApiLevel getRequiredCompilationApiLevel() {
+    return topLevelFlags.getRequiredCompilationApiLevel();
   }
 
+  @Override
   public String getSynthesizedLibraryClassesPackagePrefix() {
     return topLevelFlags.getSynthesizedLibraryClassesPackagePrefix();
   }
@@ -84,6 +88,7 @@
     return topLevelFlags.getIdentifier();
   }
 
+  @Override
   public String getJsonSource() {
     return topLevelFlags.getJsonSource();
   }
@@ -92,6 +97,7 @@
     return topLevelFlags.supportAllCallbacksFromLibrary();
   }
 
+  @Override
   public List<String> getExtraKeepRules() {
     return topLevelFlags.getExtraKeepRules();
   }
@@ -216,10 +222,6 @@
     return false;
   }
 
-  public AndroidApiLevel getRequiredCompilationApiLevel() {
-    return topLevelFlags.getRequiredCompilationAPILevel();
-  }
-
   @Override
   public MachineDesugaredLibrarySpecification toMachineSpecification(
       DexApplication app, Timing timing) throws IOException {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecificationParser.java
index 6cdd123..031ca29 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecificationParser.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification;
 
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser.CONFIGURATION_FORMAT_VERSION_KEY;
 import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.AMEND_LIBRARY_FIELD_KEY;
 import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.AMEND_LIBRARY_METHOD_KEY;
 import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.API_GENERIC_TYPES_CONVERSION_KEY;
@@ -62,6 +63,9 @@
 
 public class MachineDesugaredLibrarySpecificationParser {
 
+  private static final int MIN_SUPPORTED_VERSION = 200;
+  private static final int MAX_SUPPORTED_VERSION = 200;
+
   private static final String ERROR_MESSAGE_PREFIX = "Invalid desugared library specification: ";
 
   private final DexItemFactory dexItemFactory;
@@ -127,6 +131,19 @@
       Origin origin, String jsonConfigString, JsonObject jsonConfig) {
     this.origin = origin;
     this.jsonConfig = jsonConfig;
+    int machineVersion = required(jsonConfig, CONFIGURATION_FORMAT_VERSION_KEY).getAsInt();
+    if (machineVersion < MIN_SUPPORTED_VERSION || machineVersion > MAX_SUPPORTED_VERSION) {
+      throw reporter.fatalError(
+          new StringDiagnostic(
+              "Unsupported machine version number "
+                  + machineVersion
+                  + " not in ["
+                  + MIN_SUPPORTED_VERSION
+                  + ","
+                  + MAX_SUPPORTED_VERSION
+                  + "]",
+              origin));
+    }
     MachineTopLevelFlags topLevelFlags = parseTopLevelFlags(jsonConfigString);
     parsePackageMap();
     MachineRewritingFlags rewritingFlags = parseRewritingFlags();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java
index 389705b..c5c1b4b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineTopLevelFlags.java
@@ -44,7 +44,7 @@
     this.extraKeepRules = extraKeepRules;
   }
 
-  public AndroidApiLevel getRequiredCompilationAPILevel() {
+  public AndroidApiLevel getRequiredCompilationApiLevel() {
     return requiredCompilationAPILevel;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter.java
index e55127c..f5d81fa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification;
 
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser.CONFIGURATION_FORMAT_VERSION_KEY;
 import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.AMEND_LIBRARY_FIELD_KEY;
 import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.AMEND_LIBRARY_METHOD_KEY;
 import static com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineSpecificationJsonPool.API_GENERIC_TYPES_CONVERSION_KEY;
@@ -55,6 +56,8 @@
 
 public class MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter {
 
+  private static final int MACHINE_VERSION_NUMBER = 200;
+
   private final DexItemFactory factory;
   private final Map<String, String> packageMap = new TreeMap<>();
   private static final String chars =
@@ -78,7 +81,7 @@
     HashMap<String, Object> toJson = new LinkedHashMap<>();
 
     exportTopLevelFlags(machineSpec.getTopLevelFlags(), toJson);
-    // TODO(): export format version for compat internal to R8.
+    toJson.put(CONFIGURATION_FORMAT_VERSION_KEY, MACHINE_VERSION_NUMBER);
 
     toJson.put(COMMON_FLAGS_KEY, rewritingFlagsToString(machineSpec.getCommonFlags()));
     toJson.put(PROGRAM_FLAGS_KEY, rewritingFlagsToString(machineSpec.getProgramFlags()));
@@ -95,7 +98,7 @@
     toJson.put(IDENTIFIER_KEY, topLevelFlags.getIdentifier());
     toJson.put(
         REQUIRED_COMPILATION_API_LEVEL_KEY,
-        topLevelFlags.getRequiredCompilationAPILevel().getLevel());
+        topLevelFlags.getRequiredCompilationApiLevel().getLevel());
     toJson.put(
         SYNTHESIZED_LIBRARY_CLASSES_PACKAGE_PREFIX_KEY,
         topLevelFlags.getSynthesizedLibraryClassesPackagePrefix());
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
index ec8c5f7..cd5c568 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
@@ -253,8 +253,8 @@
     assertEquals(kr1, kr2);
     assertEquals(topLevelFlags1.getIdentifier(), topLevelFlags2.getIdentifier());
     assertEquals(
-        topLevelFlags1.getRequiredCompilationAPILevel().getLevel(),
-        topLevelFlags2.getRequiredCompilationAPILevel().getLevel());
+        topLevelFlags1.getRequiredCompilationApiLevel().getLevel(),
+        topLevelFlags2.getRequiredCompilationApiLevel().getLevel());
     assertEquals(
         topLevelFlags1.getSynthesizedLibraryClassesPackagePrefix(),
         topLevelFlags2.getSynthesizedLibraryClassesPackagePrefix());