| // Copyright (c) 2019, 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.ir.desugar; | 
 |  | 
 | import com.android.tools.r8.StringResource; | 
 | import com.android.tools.r8.graph.DexItemFactory; | 
 | import com.android.tools.r8.origin.Origin; | 
 | import com.android.tools.r8.utils.AndroidApiLevel; | 
 | import com.android.tools.r8.utils.ExceptionDiagnostic; | 
 | import com.android.tools.r8.utils.Reporter; | 
 | import com.android.tools.r8.utils.SemanticVersion; | 
 | import com.android.tools.r8.utils.StringDiagnostic; | 
 | import com.google.gson.JsonArray; | 
 | import com.google.gson.JsonElement; | 
 | import com.google.gson.JsonObject; | 
 | import com.google.gson.JsonParser; | 
 | import java.util.ArrayList; | 
 | import java.util.List; | 
 | import java.util.Map; | 
 |  | 
 | public class DesugaredLibraryConfigurationParser { | 
 |  | 
 |   public static final int MAX_SUPPORTED_VERSION = 4; | 
 |   public static final SemanticVersion MIN_SUPPORTED_VERSION = new SemanticVersion(1, 0, 9); | 
 |  | 
 |   static final String CONFIGURATION_FORMAT_VERSION_KEY = "configuration_format_version"; | 
 |   static final String VERSION_KEY = "version"; | 
 |   static final String GROUP_ID_KEY = "group_id"; | 
 |   static final String ARTIFACT_ID_KEY = "artifact_id"; | 
 |   static final String REQUIRED_COMPILATION_API_LEVEL_KEY = "required_compilation_api_level"; | 
 |   static final String SYNTHESIZED_LIBRARY_CLASSES_PACKAGE_PREFIX_KEY = | 
 |       "synthesized_library_classes_package_prefix"; | 
 |  | 
 |   static final String COMMON_FLAGS_KEY = "common_flags"; | 
 |   static final String LIBRARY_FLAGS_KEY = "library_flags"; | 
 |   static final String PROGRAM_FLAGS_KEY = "program_flags"; | 
 |  | 
 |   static final String API_LEVEL_BELOW_OR_EQUAL_KEY = "api_level_below_or_equal"; | 
 |   static final String WRAPPER_CONVERSION_KEY = "wrapper_conversion"; | 
 |   static final String CUSTOM_CONVERSION_KEY = "custom_conversion"; | 
 |   static final String REWRITE_PREFIX_KEY = "rewrite_prefix"; | 
 |   static final String RETARGET_LIB_MEMBER_KEY = "retarget_lib_member"; | 
 |   static final String EMULATE_INTERFACE_KEY = "emulate_interface"; | 
 |   static final String DONT_REWRITE_KEY = "dont_rewrite"; | 
 |   static final String BACKPORT_KEY = "backport"; | 
 |   static final String SHRINKER_CONFIG_KEY = "shrinker_config"; | 
 |  | 
 |   private final DexItemFactory dexItemFactory; | 
 |   private final Reporter reporter; | 
 |   private final boolean libraryCompilation; | 
 |   private final int minAPILevel; | 
 |  | 
 |   private DesugaredLibraryConfiguration.Builder configurationBuilder = null; | 
 |   private Origin origin; | 
 |  | 
 |   public DesugaredLibraryConfigurationParser( | 
 |       DexItemFactory dexItemFactory, | 
 |       Reporter reporter, | 
 |       boolean libraryCompilation, | 
 |       int minAPILevel) { | 
 |     this.dexItemFactory = dexItemFactory; | 
 |     this.reporter = reporter; | 
 |     this.minAPILevel = minAPILevel; | 
 |     this.libraryCompilation = libraryCompilation; | 
 |   } | 
 |  | 
 |   private JsonElement required(JsonObject json, String key) { | 
 |     if (!json.has(key)) { | 
 |       throw reporter.fatalError( | 
 |           new StringDiagnostic( | 
 |               "Invalid desugared library configuration. Expected required key '" + key + "'", | 
 |               origin)); | 
 |     } | 
 |     return json.get(key); | 
 |   } | 
 |  | 
 |   public DesugaredLibraryConfiguration parse(StringResource stringResource) { | 
 |     origin = stringResource.getOrigin(); | 
 |     assert origin != null; | 
 |     configurationBuilder = DesugaredLibraryConfiguration.builder(dexItemFactory, reporter, origin); | 
 |     if (libraryCompilation) { | 
 |       configurationBuilder.setLibraryCompilation(); | 
 |     } else { | 
 |       configurationBuilder.setProgramCompilation(); | 
 |     } | 
 |     JsonObject jsonConfig; | 
 |     try { | 
 |       String jsonConfigString = stringResource.getString(); | 
 |       JsonParser parser = new JsonParser(); | 
 |       jsonConfig = parser.parse(jsonConfigString).getAsJsonObject(); | 
 |     } catch (Exception e) { | 
 |       throw reporter.fatalError(new ExceptionDiagnostic(e, origin)); | 
 |     } | 
 |  | 
 |     JsonElement formatVersionElement = required(jsonConfig, CONFIGURATION_FORMAT_VERSION_KEY); | 
 |     int formatVersion = formatVersionElement.getAsInt(); | 
 |     if (formatVersion > MAX_SUPPORTED_VERSION) { | 
 |       throw reporter.fatalError( | 
 |           new StringDiagnostic( | 
 |               "Unsupported desugared library configuration version, please upgrade the D8/R8" | 
 |                   + " compiler.", | 
 |               origin)); | 
 |     } | 
 |  | 
 |     String version = required(jsonConfig, VERSION_KEY).getAsString(); | 
 |     SemanticVersion semanticVersion = SemanticVersion.parse(version); | 
 |     if (!semanticVersion.isNewerOrEqual(MIN_SUPPORTED_VERSION)) { | 
 |       throw reporter.fatalError( | 
 |           new StringDiagnostic( | 
 |               "Unsupported desugared library version: " | 
 |                   + version | 
 |                   + ", please upgrade the desugared library to at least version " | 
 |                   + MIN_SUPPORTED_VERSION | 
 |                   + ".", | 
 |               origin)); | 
 |     } | 
 |  | 
 |     String groupID = required(jsonConfig, GROUP_ID_KEY).getAsString(); | 
 |     String artifactID = required(jsonConfig, ARTIFACT_ID_KEY).getAsString(); | 
 |     String identifier = String.join(":", groupID, artifactID, version); | 
 |     configurationBuilder.setDesugaredLibraryIdentifier(identifier); | 
 |     configurationBuilder.setSynthesizedLibraryClassesPackagePrefix( | 
 |         required(jsonConfig, SYNTHESIZED_LIBRARY_CLASSES_PACKAGE_PREFIX_KEY).getAsString()); | 
 |  | 
 |     int required_compilation_api_level = | 
 |         required(jsonConfig, REQUIRED_COMPILATION_API_LEVEL_KEY).getAsInt(); | 
 |     configurationBuilder.setRequiredCompilationAPILevel( | 
 |         AndroidApiLevel.getAndroidApiLevel(required_compilation_api_level)); | 
 |     JsonElement commonFlags = required(jsonConfig, COMMON_FLAGS_KEY); | 
 |     JsonElement libraryFlags = required(jsonConfig, LIBRARY_FLAGS_KEY); | 
 |     JsonElement programFlags = required(jsonConfig, PROGRAM_FLAGS_KEY); | 
 |     parseFlagsList(commonFlags.getAsJsonArray()); | 
 |     parseFlagsList( | 
 |         libraryCompilation ? libraryFlags.getAsJsonArray() : programFlags.getAsJsonArray()); | 
 |     if (jsonConfig.has(SHRINKER_CONFIG_KEY)) { | 
 |       JsonArray jsonKeepRules = jsonConfig.get(SHRINKER_CONFIG_KEY).getAsJsonArray(); | 
 |       List<String> extraKeepRules = new ArrayList<>(jsonKeepRules.size()); | 
 |       for (JsonElement keepRule : jsonKeepRules) { | 
 |         extraKeepRules.add(keepRule.getAsString()); | 
 |       } | 
 |       configurationBuilder.setExtraKeepRules(extraKeepRules); | 
 |     } | 
 |     DesugaredLibraryConfiguration config = configurationBuilder.build(); | 
 |     configurationBuilder = null; | 
 |     origin = null; | 
 |     return config; | 
 |   } | 
 |  | 
 |   private void parseFlagsList(JsonArray jsonFlags) { | 
 |     for (JsonElement jsonFlagSet : jsonFlags) { | 
 |       JsonObject flag = jsonFlagSet.getAsJsonObject(); | 
 |       int api_level_below_or_equal = required(flag, API_LEVEL_BELOW_OR_EQUAL_KEY).getAsInt(); | 
 |       if (minAPILevel <= api_level_below_or_equal) { | 
 |         parseFlags(flag); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   private void parseFlags(JsonObject jsonFlagSet) { | 
 |     if (jsonFlagSet.has(REWRITE_PREFIX_KEY)) { | 
 |       for (Map.Entry<String, JsonElement> rewritePrefix : | 
 |           jsonFlagSet.get(REWRITE_PREFIX_KEY).getAsJsonObject().entrySet()) { | 
 |         configurationBuilder.putRewritePrefix( | 
 |             rewritePrefix.getKey(), rewritePrefix.getValue().getAsString()); | 
 |       } | 
 |     } | 
 |     if (jsonFlagSet.has(RETARGET_LIB_MEMBER_KEY)) { | 
 |       for (Map.Entry<String, JsonElement> retarget : | 
 |           jsonFlagSet.get(RETARGET_LIB_MEMBER_KEY).getAsJsonObject().entrySet()) { | 
 |         configurationBuilder.putRetargetCoreLibMember( | 
 |             retarget.getKey(), retarget.getValue().getAsString()); | 
 |       } | 
 |     } | 
 |     if (jsonFlagSet.has(BACKPORT_KEY)) { | 
 |       for (Map.Entry<String, JsonElement> backport : | 
 |           jsonFlagSet.get(BACKPORT_KEY).getAsJsonObject().entrySet()) { | 
 |         configurationBuilder.putBackportCoreLibraryMember( | 
 |             backport.getKey(), backport.getValue().getAsString()); | 
 |       } | 
 |     } | 
 |     if (jsonFlagSet.has(EMULATE_INTERFACE_KEY)) { | 
 |       for (Map.Entry<String, JsonElement> itf : | 
 |           jsonFlagSet.get(EMULATE_INTERFACE_KEY).getAsJsonObject().entrySet()) { | 
 |         configurationBuilder.putEmulateLibraryInterface(itf.getKey(), itf.getValue().getAsString()); | 
 |       } | 
 |     } | 
 |     if (jsonFlagSet.has(CUSTOM_CONVERSION_KEY)) { | 
 |       for (Map.Entry<String, JsonElement> conversion : | 
 |           jsonFlagSet.get(CUSTOM_CONVERSION_KEY).getAsJsonObject().entrySet()) { | 
 |         configurationBuilder.putCustomConversion( | 
 |             conversion.getKey(), conversion.getValue().getAsString()); | 
 |       } | 
 |     } | 
 |     if (jsonFlagSet.has(WRAPPER_CONVERSION_KEY)) { | 
 |       for (JsonElement wrapper : jsonFlagSet.get(WRAPPER_CONVERSION_KEY).getAsJsonArray()) { | 
 |         configurationBuilder.addWrapperConversion(wrapper.getAsString()); | 
 |       } | 
 |     } | 
 |     if (jsonFlagSet.has(DONT_REWRITE_KEY)) { | 
 |       JsonArray dontRewrite = jsonFlagSet.get(DONT_REWRITE_KEY).getAsJsonArray(); | 
 |       for (JsonElement rewrite : dontRewrite) { | 
 |         configurationBuilder.addDontRewriteInvocation(rewrite.getAsString()); | 
 |       } | 
 |     } | 
 |   } | 
 | } |