| // 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()); |
| } |
| } |
| } |
| } |