Fix desugared library human exporter
This is used to canonicalize flags to limit
diffs with internal flags.
Change-Id: I3547cd4baa3ef12e7d85303fcfa3775ea3459b9e
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
index d77a0bc..736632a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser.CONFIGURATION_FORMAT_VERSION_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.AMEND_LIBRARY_METHOD_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.API_GENERIC_TYPES_CONVERSION;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.API_LEVEL_BELOW_OR_EQUAL_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.API_LEVEL_GREATER_OR_EQUAL_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.BACKPORT_KEY;
@@ -14,13 +15,17 @@
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.CUSTOM_CONVERSION_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.DONT_RETARGET_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.DONT_REWRITE_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.DONT_REWRITE_PREFIX_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.EMULATE_INTERFACE_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.IDENTIFIER_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.LIBRARY_FLAGS_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.MAINTAIN_PREFIX_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.NEVER_OUTLINE_API_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.PROGRAM_FLAGS_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.REQUIRED_COMPILATION_API_LEVEL_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.RETARGET_METHOD_EMULATED_DISPATCH_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.RETARGET_METHOD_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.RETARGET_STATIC_FIELD_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.REWRITE_DERIVED_PREFIX_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.REWRITE_PREFIX_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.SHRINKER_CONFIG_KEY;
@@ -32,13 +37,15 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AccessFlags;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.ir.desugar.desugaredlibrary.ApiLevelRange;
import com.google.gson.Gson;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
@@ -102,12 +109,21 @@
.forEach((k, v) -> rewriteDerivedPrefix.put(k, new TreeMap<>(v)));
toJson.put(REWRITE_DERIVED_PREFIX_KEY, rewriteDerivedPrefix);
}
+ if (!flags.getDontRewritePrefix().isEmpty()) {
+ toJson.put(DONT_REWRITE_PREFIX_KEY, stringSetToString(flags.getDontRewritePrefix()));
+ }
+ if (!flags.getMaintainPrefix().isEmpty()) {
+ toJson.put(MAINTAIN_PREFIX_KEY, stringSetToString(flags.getMaintainPrefix()));
+ }
if (!flags.getEmulatedInterfaces().isEmpty()) {
toJson.put(EMULATE_INTERFACE_KEY, mapToString(flags.getEmulatedInterfaces()));
}
if (!flags.getDontRewriteInvocation().isEmpty()) {
toJson.put(DONT_REWRITE_KEY, setToString(flags.getDontRewriteInvocation()));
}
+ if (!flags.getRetargetStaticField().isEmpty()) {
+ toJson.put(RETARGET_STATIC_FIELD_KEY, mapToString(flags.getRetargetStaticField()));
+ }
if (!flags.getRetargetMethod().isEmpty()) {
toJson.put(RETARGET_METHOD_KEY, mapToString(flags.getRetargetMethod()));
}
@@ -122,15 +138,24 @@
if (!flags.getLegacyBackport().isEmpty()) {
toJson.put(BACKPORT_KEY, mapToString(flags.getLegacyBackport()));
}
+ if (!flags.getApiGenericConversion().isEmpty()) {
+ toJson.put(API_GENERIC_TYPES_CONVERSION, mapArrayToString(flags.getApiGenericConversion()));
+ }
if (!flags.getWrapperConversions().isEmpty()) {
registerWrapperConversions(toJson, flags.getWrapperConversions());
}
if (!flags.getCustomConversions().isEmpty()) {
toJson.put(CUSTOM_CONVERSION_KEY, mapToString(flags.getCustomConversions()));
}
+ if (!flags.getNeverOutlineApi().isEmpty()) {
+ toJson.put(NEVER_OUTLINE_API_KEY, setToString(flags.getNeverOutlineApi()));
+ }
if (!flags.getAmendLibraryMethod().isEmpty()) {
toJson.put(AMEND_LIBRARY_METHOD_KEY, amendLibraryToString(flags.getAmendLibraryMethod()));
}
+ if (!flags.getAmendLibraryField().isEmpty()) {
+ toJson.put(AMEND_LIBRARY_METHOD_KEY, amendLibraryToString(flags.getAmendLibraryField()));
+ }
list.add(toJson);
}
return list;
@@ -148,20 +173,32 @@
stringMap.put(toString(k), setToString(v));
}
});
- toJson.put(WRAPPER_CONVERSION_KEY, stringSet);
- toJson.put(WRAPPER_CONVERSION_EXCLUDING_KEY, stringMap);
+ if (!stringSet.isEmpty()) {
+ toJson.put(WRAPPER_CONVERSION_KEY, stringSet);
+ }
+ if (!stringMap.isEmpty()) {
+ toJson.put(WRAPPER_CONVERSION_EXCLUDING_KEY, stringMap);
+ }
}
- private List<String> amendLibraryToString(Map<DexMethod, MethodAccessFlags> amendLibraryMembers) {
+ private List<String> amendLibraryToString(
+ Map<? extends DexItem, ? extends AccessFlags<?>> amendLibraryMembers) {
List<String> stringSet = new ArrayList<>();
amendLibraryMembers.forEach(
(member, flags) -> stringSet.add(flags.toString() + " " + toString(member)));
return stringSet;
}
+ private List<String> stringSetToString(Set<String> set) {
+ List<String> stringSet = new ArrayList<>(set);
+ Collections.sort(stringSet);
+ return stringSet;
+ }
+
private List<String> setToString(Set<? extends DexItem> set) {
List<String> stringSet = new ArrayList<>();
set.forEach(e -> stringSet.add(toString(e)));
+ Collections.sort(stringSet);
return stringSet;
}
@@ -171,10 +208,36 @@
return stringMap;
}
+ private Map<String, Object[]> mapArrayToString(Map<? extends DexItem, DexMethod[]> map) {
+ Map<String, Object[]> stringMap = new TreeMap<>();
+ map.forEach((k, v) -> stringMap.put(toString(k), arrayToString(v)));
+ return stringMap;
+ }
+
+ private Object[] arrayToString(DexMethod[] array) {
+ List<Object> res = new ArrayList<>();
+ int last = array.length - 1;
+ if (array[last] != null) {
+ res.add(-1);
+ res.add(toString(array[last]));
+ }
+ for (int i = 0; i < array.length - 1; i++) {
+ if (array[i] != null) {
+ res.add(i);
+ res.add(toString(array[i]));
+ }
+ }
+ return res.toArray();
+ }
+
private String toString(DexItem o) {
if (o instanceof DexType) {
return o.toString();
}
+ if (o instanceof DexField) {
+ DexField field = (DexField) o;
+ return field.getType() + " " + field.getHolderType() + "#" + field.getName();
+ }
if (o instanceof DexMethod) {
DexMethod method = (DexMethod) o;
StringBuilder sb =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/DesugaredLibraryConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/DesugaredLibraryConverter.java
index 34a8e9d..e14a21e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/DesugaredLibraryConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/DesugaredLibraryConverter.java
@@ -73,7 +73,7 @@
Files.write(output, Collections.singleton(outputString));
}
- private static String convertToMachineSpecification(
+ public static String convertToMachineSpecification(
InternalOptions options,
DexApplication appForConversion,
MultiAPILevelHumanDesugaredLibrarySpecification humanSpec)
@@ -88,7 +88,7 @@
return machineJson.get();
}
- private static JsonObject parseJsonConfig(InternalOptions options, FileResource jsonResource) {
+ public static JsonObject parseJsonConfig(InternalOptions options, FileResource jsonResource) {
JsonObject jsonConfig;
try {
String jsonConfigString = jsonResource.getString();
@@ -104,7 +104,7 @@
* Parse the human specification, or parse and convert the legacy specification into human
* specification.
*/
- private static MultiAPILevelHumanDesugaredLibrarySpecification getInputAsHumanSpecification(
+ public static MultiAPILevelHumanDesugaredLibrarySpecification getInputAsHumanSpecification(
InternalOptions options,
FileResource jsonResource,
JsonObject jsonConfig,
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 468494f..535d82a 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
@@ -4,12 +4,17 @@
package com.android.tools.r8.desugar.desugaredlibrary.specification;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser.isMachineSpecification;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.DesugaredLibraryConverter.convertToMachineSpecification;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.DesugaredLibraryConverter.getInputAsHumanSpecification;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.DesugaredLibraryConverter.parseJsonConfig;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.StringResource;
+import com.android.tools.r8.StringResource.FileResource;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
@@ -32,7 +37,6 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineTopLevelFlags;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MultiAPILevelMachineDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.DesugaredLibraryConverter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.HumanToMachineSpecificationConverter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LegacyToHumanSpecificationConverter;
import com.android.tools.r8.origin.Origin;
@@ -41,8 +45,11 @@
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
+import com.google.gson.JsonObject;
import java.io.IOException;
+import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.Collections;
import java.util.Map;
import org.junit.Assume;
import org.junit.Test;
@@ -126,11 +133,11 @@
private void testMultiLevelUsingMain(LibraryDesugaringSpecification spec) throws IOException {
Assume.assumeTrue(ToolHelper.isLocalDevelopment());
- Path output = temp.newFile().toPath();
- DesugaredLibraryConverter.convertMultiLevelAnythingToMachineSpecification(
- spec.getSpecification(), spec.getDesugarJdkLibs(), spec.getLibraryFiles(), output);
-
InternalOptions options = new InternalOptions();
+
+ Path output = temp.newFile().toPath();
+ convertMultiLevelAnythingToMachineSpecification(spec, output, options);
+
MachineDesugaredLibrarySpecification machineSpecParsed =
new MachineDesugaredLibrarySpecificationParser(
options.dexItemFactory(),
@@ -142,6 +149,36 @@
assertFalse(machineSpecParsed.getRewriteType().isEmpty());
}
+ public void convertMultiLevelAnythingToMachineSpecification(
+ LibraryDesugaringSpecification spec, Path output, InternalOptions options)
+ throws IOException {
+
+ FileResource jsonResource = StringResource.fromFile(spec.getSpecification());
+ JsonObject jsonConfig = parseJsonConfig(options, jsonResource);
+
+ if (isMachineSpecification(jsonConfig, options.reporter, jsonResource.getOrigin())) {
+ // Nothing to convert;
+ Files.copy(spec.getSpecification(), output);
+ return;
+ }
+
+ DexApplication appForConversion = spec.getAppForTesting(options, true);
+ MultiAPILevelHumanDesugaredLibrarySpecification humanSpec =
+ getInputAsHumanSpecification(options, jsonResource, jsonConfig, appForConversion);
+ String outputString = convertToMachineSpecification(options, appForConversion, humanSpec);
+ Files.write(output, Collections.singleton(outputString));
+
+ // Validate the written spec is the read spec.
+ Box<String> json = new Box<>();
+ MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.export(
+ humanSpec, ((string, handler) -> json.set(string)));
+ MultiAPILevelHumanDesugaredLibrarySpecification writtenHumanSpec =
+ new MultiAPILevelHumanDesugaredLibrarySpecificationParser(
+ options.dexItemFactory(), options.reporter)
+ .parseMultiLevelConfiguration(StringResource.fromString(json.get(), Origin.unknown()));
+ assertSpecEquals(humanSpec, writtenHumanSpec);
+ }
+
@Test
public void testSingleLevel() throws IOException {
Assume.assumeTrue(ToolHelper.isLocalDevelopment());