Specify wrapper types in the desugared library configuration.
Bug: 158645207
Change-Id: I2d55f89e378789e0b9e3256acbfd3b05c3fc41cb
diff --git a/src/library_desugar/desugar_jdk_libs.json b/src/library_desugar/desugar_jdk_libs.json
index 8c023aa..6477eff 100644
--- a/src/library_desugar/desugar_jdk_libs.json
+++ b/src/library_desugar/desugar_jdk_libs.json
@@ -1,10 +1,105 @@
{
- "configuration_format_version": 4,
+ "configuration_format_version": 5,
"group_id" : "com.tools.android",
"artifact_id" : "desugar_jdk_libs",
- "version": "0.12.0",
+ "version": "1.0.7",
"required_compilation_api_level": 26,
"synthesized_library_classes_package_prefix": "j$.",
+ "common_flags": [
+ {
+ "api_level_below_or_equal": 25,
+ "custom_conversion": {
+ "java.time.ZonedDateTime": "java.time.TimeConversions",
+ "java.time.LocalDate": "java.time.TimeConversions",
+ "java.time.Duration": "java.time.TimeConversions",
+ "java.time.ZoneId": "java.time.TimeConversions",
+ "java.time.MonthDay": "java.time.TimeConversions",
+ "java.time.Instant": "java.time.TimeConversions"
+ },
+ "wrapper_conversion": [
+ "java.time.Clock"
+ ]
+ },
+ {
+ "api_level_below_or_equal": 23,
+ "dont_rewrite": [
+ "java.util.Iterator#remove"
+ ],
+ "emulate_interface": {
+ "java.lang.Iterable": "j$.lang.Iterable",
+ "java.util.Map$Entry": "j$.util.Map$Entry",
+ "java.util.Collection": "j$.util.Collection",
+ "java.util.Map": "j$.util.Map",
+ "java.util.Iterator": "j$.util.Iterator",
+ "java.util.Comparator": "j$.util.Comparator",
+ "java.util.List": "j$.util.List",
+ "java.util.SortedSet": "j$.util.SortedSet",
+ "java.util.Set": "j$.util.Set",
+ "java.util.concurrent.ConcurrentMap": "j$.util.concurrent.ConcurrentMap"
+ },
+ "custom_conversion": {
+ "java.util.Optional": "java.util.OptionalConversions",
+ "java.util.OptionalDouble": "java.util.OptionalConversions",
+ "java.util.OptionalInt": "java.util.OptionalConversions",
+ "java.util.OptionalLong": "java.util.OptionalConversions",
+ "java.util.LongSummaryStatistics": "java.util.LongSummaryStatisticsConversions",
+ "java.util.IntSummaryStatistics": "java.util.IntSummaryStatisticsConversions",
+ "java.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatisticsConversions"
+ },
+ "wrapper_conversion": [
+ "java.util.PrimitiveIterator$OfDouble",
+ "java.util.PrimitiveIterator$OfInt",
+ "java.util.PrimitiveIterator$OfLong",
+ "java.util.Spliterator",
+ "java.util.Spliterator$OfDouble",
+ "java.util.Spliterator$OfInt",
+ "java.util.Spliterator$OfLong",
+ "java.util.Spliterator$OfPrimitive",
+ "java.util.function.BiConsumer",
+ "java.util.function.BiFunction",
+ "java.util.function.BiPredicate",
+ "java.util.function.BinaryOperator",
+ "java.util.function.Consumer",
+ "java.util.function.DoubleBinaryOperator",
+ "java.util.function.DoubleConsumer",
+ "java.util.function.DoubleFunction",
+ "java.util.function.DoublePredicate",
+ "java.util.function.DoubleToIntFunction",
+ "java.util.function.DoubleToLongFunction",
+ "java.util.function.DoubleUnaryOperator",
+ "java.util.function.Function",
+ "java.util.function.IntBinaryOperator",
+ "java.util.function.IntConsumer",
+ "java.util.function.IntFunction",
+ "java.util.function.IntPredicate",
+ "java.util.function.IntToDoubleFunction",
+ "java.util.function.IntToLongFunction",
+ "java.util.function.IntUnaryOperator",
+ "java.util.function.LongBinaryOperator",
+ "java.util.function.LongConsumer",
+ "java.util.function.LongFunction",
+ "java.util.function.LongPredicate",
+ "java.util.function.LongToDoubleFunction",
+ "java.util.function.LongToIntFunction",
+ "java.util.function.LongUnaryOperator",
+ "java.util.function.ObjDoubleConsumer",
+ "java.util.function.ObjIntConsumer",
+ "java.util.function.ObjLongConsumer",
+ "java.util.function.Predicate",
+ "java.util.function.Supplier",
+ "java.util.function.ToDoubleFunction",
+ "java.util.function.ToIntFunction",
+ "java.util.function.ToLongFunction",
+ "java.util.function.UnaryOperator",
+ "java.util.stream.BaseStream",
+ "java.util.stream.Collector",
+ "java.util.stream.DoubleStream",
+ "java.util.stream.IntStream",
+ "java.util.stream.LongStream",
+ "java.util.stream.Stream"
+ ]
+ }
+ ],
"library_flags": [
{
"api_level_below_or_equal": 25,
@@ -53,30 +148,6 @@
"java.util.Arrays#stream": "java.util.DesugarArrays",
"java.util.Arrays#spliterator": "java.util.DesugarArrays",
"java.util.LinkedHashSet#spliterator": "java.util.DesugarLinkedHashSet"
- },
- "dont_rewrite": [
- "java.util.Iterator#remove"
- ],
- "emulate_interface": {
- "java.lang.Iterable": "j$.lang.Iterable",
- "java.util.Map$Entry": "j$.util.Map$Entry",
- "java.util.Collection": "j$.util.Collection",
- "java.util.Map": "j$.util.Map",
- "java.util.Iterator": "j$.util.Iterator",
- "java.util.Comparator": "j$.util.Comparator",
- "java.util.List": "j$.util.List",
- "java.util.SortedSet": "j$.util.SortedSet",
- "java.util.Set": "j$.util.Set",
- "java.util.concurrent.ConcurrentMap": "j$.util.concurrent.ConcurrentMap"
- },
- "custom_conversion": {
- "java.util.Optional": "java.util.OptionalConversions",
- "java.util.OptionalDouble": "java.util.OptionalConversions",
- "java.util.OptionalInt": "java.util.OptionalConversions",
- "java.util.OptionalLong": "java.util.OptionalConversions",
- "java.util.LongSummaryStatistics": "java.util.LongSummaryStatisticsConversions",
- "java.util.IntSummaryStatistics": "java.util.IntSummaryStatisticsConversions",
- "java.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatisticsConversions"
}
}
],
@@ -93,14 +164,6 @@
"java.util.Date#toInstant": "java.util.DesugarDate",
"java.util.GregorianCalendar#from": "java.util.DesugarGregorianCalendar",
"java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar"
- },
- "custom_conversion": {
- "java.time.ZonedDateTime": "java.time.TimeConversions",
- "java.time.LocalDate": "java.time.TimeConversions",
- "java.time.Duration": "java.time.TimeConversions",
- "java.time.ZoneId": "java.time.TimeConversions",
- "java.time.MonthDay": "java.time.TimeConversions",
- "java.time.Instant": "java.time.TimeConversions"
}
},
{
@@ -137,30 +200,6 @@
"java.util.concurrent.atomic.AtomicReference#accumulateAndGet": "java.util.concurrent.atomic.DesugarAtomicReference",
"java.util.Collections#synchronizedMap": "java.util.DesugarCollections",
"java.util.Collections#synchronizedSortedMap": "java.util.DesugarCollections"
- },
- "dont_rewrite": [
- "java.util.Iterator#remove"
- ],
- "emulate_interface": {
- "java.lang.Iterable": "j$.lang.Iterable",
- "java.util.Map$Entry": "j$.util.Map$Entry",
- "java.util.Collection": "j$.util.Collection",
- "java.util.Map": "j$.util.Map",
- "java.util.Iterator": "j$.util.Iterator",
- "java.util.Comparator": "j$.util.Comparator",
- "java.util.List": "j$.util.List",
- "java.util.SortedSet": "j$.util.SortedSet",
- "java.util.Set": "j$.util.Set",
- "java.util.concurrent.ConcurrentMap": "j$.util.concurrent.ConcurrentMap"
- },
- "custom_conversion": {
- "java.util.Optional": "java.util.OptionalConversions",
- "java.util.OptionalDouble": "java.util.OptionalConversions",
- "java.util.OptionalInt": "java.util.OptionalConversions",
- "java.util.OptionalLong": "java.util.OptionalConversions",
- "java.util.LongSummaryStatistics": "java.util.LongSummaryStatisticsConversions",
- "java.util.IntSummaryStatistics": "java.util.IntSummaryStatisticsConversions",
- "java.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatisticsConversions"
}
}
],
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
index 7d5b31e..dcf33f1 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -69,7 +69,7 @@
private final Set<DexMethod> parallelMethods = Sets.newIdentityHashSet();
- private GenerateLintFiles(String desugarConfigurationPath, String outputDirectory) {
+ public GenerateLintFiles(String desugarConfigurationPath, String outputDirectory) {
this.desugaredLibraryConfiguration =
readDesugaredLibraryConfiguration(desugarConfigurationPath);
this.outputDirectory =
@@ -356,20 +356,24 @@
apiLevel >= desugaredLibraryConfiguration.getRequiredCompilationApiLevel().getLevel();
apiLevel--) {
System.out.println("Generating lint files for compile API " + apiLevel);
- generateLintFiles(
- AndroidApiLevel.getAndroidApiLevel(apiLevel),
- minApiLevel -> minApiLevel == AndroidApiLevel.L || minApiLevel == AndroidApiLevel.B,
- (minApiLevel, method) -> {
- assert minApiLevel == AndroidApiLevel.L || minApiLevel == AndroidApiLevel.B;
- if (minApiLevel == AndroidApiLevel.L) {
- return true;
- }
- assert minApiLevel == AndroidApiLevel.B;
- return !parallelMethods.contains(method.method);
- });
+ run(apiLevel);
}
}
+ public void run(int apiLevel) throws Exception {
+ generateLintFiles(
+ AndroidApiLevel.getAndroidApiLevel(apiLevel),
+ minApiLevel -> minApiLevel == AndroidApiLevel.L || minApiLevel == AndroidApiLevel.B,
+ (minApiLevel, method) -> {
+ assert minApiLevel == AndroidApiLevel.L || minApiLevel == AndroidApiLevel.B;
+ if (minApiLevel == AndroidApiLevel.L) {
+ return true;
+ }
+ assert minApiLevel == AndroidApiLevel.B;
+ return !parallelMethods.contains(method.method);
+ });
+ }
+
public static void main(String[] args) throws Exception {
if (args.length != 2) {
System.out.println("Usage: GenerateLineFiles <desuage configuration> <output directory>");
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index ddca96a..4b35d17 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -121,9 +121,6 @@
LazyLoadedDexApplication lazyApp =
new ApplicationReader(inputApp, options, timing).read(executor);
- // Store a direct lookup to determine actual library definitions for wrapper generation.
- options.desugaredLibraryBootclasspathDefinitions =
- t -> lazyApp.libraryDefintionFor(t) != null;
PrefixRewritingMapper rewritePrefix =
options.desugaredLibraryConfiguration.createPrefixRewritingMapper(options);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
index 277ba09..401297e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
@@ -288,7 +288,7 @@
SortedProgramMethodSet callbacks = generateCallbackMethods();
irConverter.processMethodsConcurrently(callbacks, executorService);
if (appView.options().isDesugaredLibraryCompilation()) {
- wrapperSynthesizor.finalizeWrappersForD8(builder, irConverter, executorService);
+ wrapperSynthesizor.finalizeWrappersForL8(builder, irConverter, executorService);
}
}
@@ -349,26 +349,23 @@
}
public void reportInvalidInvoke(DexType type, DexMethod invokedMethod, String debugString) {
- if (appView.options().isDesugaredLibraryCompilation()) {
- // TODO(b/158645207): If wrappers are exactly specified this should fail both for normal
- // compilation and L8.
- return;
- }
DexType desugaredType = appView.rewritePrefix.rewrittenType(type, appView);
- appView
- .options()
- .reporter
- .info(
- new StringDiagnostic(
- "Invoke to "
- + invokedMethod.holder
- + "#"
- + invokedMethod.name
- + " may not work correctly at runtime (Cannot convert "
- + debugString
- + "type "
- + desugaredType
- + ")."));
+ StringDiagnostic diagnostic =
+ new StringDiagnostic(
+ "Invoke to "
+ + invokedMethod.holder
+ + "#"
+ + invokedMethod.name
+ + " may not work correctly at runtime (Cannot convert "
+ + debugString
+ + "type "
+ + desugaredType
+ + ").");
+ if (appView.options().isDesugaredLibraryCompilation()) {
+ throw appView.options().reporter.fatalError(diagnostic);
+ } else {
+ appView.options().reporter.info(diagnostic);
+ }
}
public static DexType vivifiedTypeFor(DexType type, AppView<?> appView) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
index 60cf4c6..494831e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
@@ -12,18 +12,26 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.desugar.PrefixRewritingMapper.DesugarPrefixRewritingMapper;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
public class DesugaredLibraryConfiguration {
@@ -41,9 +49,10 @@
private final Map<DexType, DexType> customConversions;
private final List<Pair<DexType, DexString>> dontRewriteInvocation;
private final List<String> extraKeepRules;
+ private final Set<DexType> wrapperConversions;
- public static Builder builder(DexItemFactory dexItemFactory) {
- return new Builder(dexItemFactory);
+ public static Builder builder(DexItemFactory dexItemFactory, Reporter reporter, Origin origin) {
+ return new Builder(dexItemFactory, reporter, origin);
}
public static DesugaredLibraryConfiguration withOnlyRewritePrefixForTesting(
@@ -58,6 +67,7 @@
ImmutableMap.of(),
ImmutableMap.of(),
ImmutableMap.of(),
+ ImmutableSet.of(),
ImmutableList.of(),
ImmutableList.of());
}
@@ -73,11 +83,12 @@
ImmutableMap.of(),
ImmutableMap.of(),
ImmutableMap.of(),
+ ImmutableSet.of(),
ImmutableList.of(),
ImmutableList.of());
}
- public DesugaredLibraryConfiguration(
+ private DesugaredLibraryConfiguration(
AndroidApiLevel requiredCompilationAPILevel,
boolean libraryCompilation,
String packagePrefix,
@@ -87,6 +98,7 @@
Map<DexString, Map<DexType, DexType>> retargetCoreLibMember,
Map<DexType, DexType> backportCoreLibraryMember,
Map<DexType, DexType> customConversions,
+ Set<DexType> wrapperConversions,
List<Pair<DexType, DexString>> dontRewriteInvocation,
List<String> extraKeepRules) {
this.requiredCompilationAPILevel = requiredCompilationAPILevel;
@@ -98,6 +110,7 @@
this.retargetCoreLibMember = retargetCoreLibMember;
this.backportCoreLibraryMember = backportCoreLibraryMember;
this.customConversions = customConversions;
+ this.wrapperConversions = wrapperConversions;
this.dontRewriteInvocation = dontRewriteInvocation;
this.extraKeepRules = extraKeepRules;
}
@@ -158,6 +171,10 @@
return customConversions;
}
+ public Set<DexType> getWrapperConversions() {
+ return wrapperConversions;
+ }
+
public List<Pair<DexType, DexString>> getDontRewriteInvocation() {
return dontRewriteInvocation;
}
@@ -169,6 +186,8 @@
public static class Builder {
private final DexItemFactory factory;
+ private final Reporter reporter;
+ private final Origin origin;
private AndroidApiLevel requiredCompilationAPILevel;
private boolean libraryCompilation = false;
@@ -176,15 +195,34 @@
FALL_BACK_SYNTHESIZED_CLASSES_PACKAGE_PREFIX;
private String identifier;
private Map<String, String> rewritePrefix = new HashMap<>();
- private Map<DexType, DexType> emulateLibraryInterface = new HashMap<>();
+ private Map<DexType, DexType> emulateLibraryInterface = new IdentityHashMap<>();
private Map<DexString, Map<DexType, DexType>> retargetCoreLibMember = new IdentityHashMap<>();
- private Map<DexType, DexType> backportCoreLibraryMember = new HashMap<>();
- private Map<DexType, DexType> customConversions = new HashMap<>();
+ private Map<DexType, DexType> backportCoreLibraryMember = new IdentityHashMap<>();
+ private Map<DexType, DexType> customConversions = new IdentityHashMap<>();
+ private Set<DexType> wrapperConversions = Sets.newIdentityHashSet();
private List<Pair<DexType, DexString>> dontRewriteInvocation = new ArrayList<>();
private List<String> extraKeepRules = Collections.emptyList();
- public Builder(DexItemFactory dexItemFactory) {
+ private Builder(DexItemFactory dexItemFactory, Reporter reporter, Origin origin) {
this.factory = dexItemFactory;
+ this.reporter = reporter;
+ this.origin = origin;
+ }
+
+ // Utility to set values. Currently assumes the key is fresh.
+ private <K, V> void put(Map<K, V> map, K key, V value, String desc) {
+ if (map.containsKey(key)) {
+ throw reporter.fatalError(
+ new StringDiagnostic(
+ "Invalid desugared library configuration. "
+ + " Duplicate assignment of key: '"
+ + key
+ + "' in sections for '"
+ + desc
+ + "'",
+ origin));
+ }
+ map.put(key, value);
}
public Builder setSynthesizedLibraryClassesPackagePrefix(String prefix) {
@@ -218,7 +256,11 @@
}
public Builder putRewritePrefix(String prefix, String rewrittenPrefix) {
- rewritePrefix.put(prefix, rewrittenPrefix);
+ put(
+ rewritePrefix,
+ prefix,
+ rewrittenPrefix,
+ DesugaredLibraryConfigurationParser.REWRITE_PREFIX_KEY);
return this;
}
@@ -226,14 +268,28 @@
String emulateLibraryItf, String rewrittenEmulateLibraryItf) {
DexType interfaceType = stringClassToDexType(emulateLibraryItf);
DexType rewrittenType = stringClassToDexType(rewrittenEmulateLibraryItf);
- emulateLibraryInterface.put(interfaceType, rewrittenType);
+ put(
+ emulateLibraryInterface,
+ interfaceType,
+ rewrittenType,
+ DesugaredLibraryConfigurationParser.EMULATE_INTERFACE_KEY);
return this;
}
public Builder putCustomConversion(String type, String conversionHolder) {
DexType dexType = stringClassToDexType(type);
DexType conversionType = stringClassToDexType(conversionHolder);
- customConversions.put(dexType, conversionType);
+ put(
+ customConversions,
+ dexType,
+ conversionType,
+ DesugaredLibraryConfigurationParser.CUSTOM_CONVERSION_KEY);
+ return this;
+ }
+
+ public Builder addWrapperConversion(String type) {
+ DexType dexType = stringClassToDexType(type);
+ wrapperConversions.add(dexType);
return this;
}
@@ -245,14 +301,22 @@
DexType originalType = stringClassToDexType(retarget.substring(0, index));
DexType finalType = stringClassToDexType(rewrittenRetarget);
assert !typeMap.containsKey(originalType);
- typeMap.put(originalType, finalType);
+ put(
+ typeMap,
+ originalType,
+ finalType,
+ DesugaredLibraryConfigurationParser.RETARGET_LIB_MEMBER_KEY);
return this;
}
public Builder putBackportCoreLibraryMember(String backport, String rewrittenBackport) {
DexType backportType = stringClassToDexType(backport);
DexType rewrittenBackportType = stringClassToDexType(rewrittenBackport);
- backportCoreLibraryMember.put(backportType, rewrittenBackportType);
+ put(
+ backportCoreLibraryMember,
+ backportType,
+ rewrittenBackportType,
+ DesugaredLibraryConfigurationParser.BACKPORT_KEY);
return this;
}
@@ -279,6 +343,7 @@
}
public DesugaredLibraryConfiguration build() {
+ validate();
return new DesugaredLibraryConfiguration(
requiredCompilationAPILevel,
libraryCompilation,
@@ -289,8 +354,22 @@
ImmutableMap.copyOf(retargetCoreLibMember),
ImmutableMap.copyOf(backportCoreLibraryMember),
ImmutableMap.copyOf(customConversions),
+ ImmutableSet.copyOf(wrapperConversions),
ImmutableList.copyOf(dontRewriteInvocation),
ImmutableList.copyOf(extraKeepRules));
}
+
+ private void validate() {
+ SetView<DexType> dups = Sets.intersection(customConversions.keySet(), wrapperConversions);
+ if (!dups.isEmpty()) {
+ throw reporter.fatalError(
+ new StringDiagnostic(
+ "Invalid desugared library configuration. "
+ + "Duplicate types in custom conversions and wrapper conversions: "
+ + String.join(
+ ", ", dups.stream().map(DexType::toString).collect(Collectors.toSet())),
+ origin));
+ }
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
index 8896170..5b57033 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
@@ -6,7 +6,9 @@
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.StringDiagnostic;
import com.google.gson.JsonArray;
@@ -19,158 +21,184 @@
public class DesugaredLibraryConfigurationParser {
- private static final int MAX_SUPPORTED_VERSION = 4;
+ public static final int MAX_SUPPORTED_VERSION = 5;
+ private static final int MIN_SUPPORTED_VERSION = 5;
- private final DesugaredLibraryConfiguration.Builder configurationBuilder;
+ private static final String MIN_DESUGARED_LIBRARY_WITH_SUPPORTED_VERSION = "1.0.7";
+
+ 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) {
+ assert MIN_SUPPORTED_VERSION <= MAX_SUPPORTED_VERSION;
+ this.dexItemFactory = dexItemFactory;
this.reporter = reporter;
- this.configurationBuilder = DesugaredLibraryConfiguration.builder(dexItemFactory);
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();
}
- }
-
- public DesugaredLibraryConfiguration parse(StringResource stringResource) {
- String jsonConfigString;
+ JsonObject jsonConfig;
try {
- jsonConfigString = stringResource.getString();
+ String jsonConfigString = stringResource.getString();
+ JsonParser parser = new JsonParser();
+ jsonConfig = parser.parse(jsonConfigString).getAsJsonObject();
} catch (Exception e) {
- throw reporter.fatalError(
- new StringDiagnostic("Cannot access desugared library configuration."));
+ throw reporter.fatalError(new ExceptionDiagnostic(e, origin));
}
- JsonParser parser = new JsonParser();
- JsonObject jsonConfig = parser.parse(jsonConfigString).getAsJsonObject();
- int formatVersion = jsonConfig.get("configuration_format_version").getAsInt();
+ 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."));
+ + " compiler.",
+ origin));
}
- if (formatVersion == 1) {
- reporter.warning(
+ if (formatVersion < MIN_SUPPORTED_VERSION) {
+ throw reporter.fatalError(
new StringDiagnostic(
- "You are using an experimental version of the desugared library configuration, "
- + "distributed only in the early canary versions. Please update for "
- + "production releases and to fix this warning."));
+ "Unsupported desugared library version, please upgrade the"
+ + " desugared library to at least version "
+ + MIN_DESUGARED_LIBRARY_WITH_SUPPORTED_VERSION
+ + ".",
+ origin));
}
- String version = jsonConfig.get("version").getAsString();
- String groupID;
- String artifactID;
- if (formatVersion < 4) {
- groupID = "com.tools.android";
- artifactID = "desugar_jdk_libs";
- } else {
- groupID = jsonConfig.get("group_id").getAsString();
- artifactID = jsonConfig.get("artifact_id").getAsString();
- }
+ String version = required(jsonConfig, VERSION_KEY).getAsString();
+ 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());
- if (jsonConfig.has("synthesized_library_classes_package_prefix")) {
- configurationBuilder.setSynthesizedLibraryClassesPackagePrefix(
- jsonConfig.get("synthesized_library_classes_package_prefix").getAsString());
- } else {
- reporter.warning(
- new StringDiagnostic(
- "Missing package_prefix, falling back to "
- + DesugaredLibraryConfiguration.FALL_BACK_SYNTHESIZED_CLASSES_PACKAGE_PREFIX
- + " prefix, update desugared library configuration."));
- }
int required_compilation_api_level =
- jsonConfig.get("required_compilation_api_level").getAsInt();
+ required(jsonConfig, REQUIRED_COMPILATION_API_LEVEL_KEY).getAsInt();
configurationBuilder.setRequiredCompilationAPILevel(
AndroidApiLevel.getAndroidApiLevel(required_compilation_api_level));
- JsonArray jsonFlags =
- libraryCompilation
- ? jsonConfig.getAsJsonArray("library_flags")
- : jsonConfig.getAsJsonArray("program_flags");
- for (JsonElement jsonFlagSet : jsonFlags) {
- int api_level_below_or_equal =
- jsonFlagSet.getAsJsonObject().get("api_level_below_or_equal").getAsInt();
- if (minAPILevel > api_level_below_or_equal) {
- continue;
- }
- parseFlags(jsonFlagSet.getAsJsonObject());
- }
- if (libraryCompilation && formatVersion <= 4) {
- // Read custom conversions from program flags.
- for (JsonElement jsonFlagSet : jsonConfig.getAsJsonArray("program_flags")) {
- JsonObject jsonFlagSetObject = jsonFlagSet.getAsJsonObject();
- int api_level_below_or_equal = jsonFlagSetObject.get("api_level_below_or_equal").getAsInt();
- if (minAPILevel > api_level_below_or_equal) {
- continue;
- }
- String customConversionKey = "custom_conversion";
- if (jsonFlagSetObject.has(customConversionKey)) {
- JsonObject backportedFlags = new JsonObject();
- backportedFlags.add(customConversionKey, jsonFlagSetObject.get(customConversionKey));
- parseFlags(backportedFlags);
- }
- }
- }
- if (jsonConfig.has("shrinker_config")) {
- JsonArray jsonKeepRules = jsonConfig.get("shrinker_config").getAsJsonArray();
+ 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);
}
- return configurationBuilder.build();
+ 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")) {
+ if (jsonFlagSet.has(REWRITE_PREFIX_KEY)) {
for (Map.Entry<String, JsonElement> rewritePrefix :
- jsonFlagSet.get("rewrite_prefix").getAsJsonObject().entrySet()) {
+ jsonFlagSet.get(REWRITE_PREFIX_KEY).getAsJsonObject().entrySet()) {
configurationBuilder.putRewritePrefix(
rewritePrefix.getKey(), rewritePrefix.getValue().getAsString());
}
}
- if (jsonFlagSet.has("retarget_lib_member")) {
+ if (jsonFlagSet.has(RETARGET_LIB_MEMBER_KEY)) {
for (Map.Entry<String, JsonElement> retarget :
- jsonFlagSet.get("retarget_lib_member").getAsJsonObject().entrySet()) {
+ jsonFlagSet.get(RETARGET_LIB_MEMBER_KEY).getAsJsonObject().entrySet()) {
configurationBuilder.putRetargetCoreLibMember(
retarget.getKey(), retarget.getValue().getAsString());
}
}
- if (jsonFlagSet.has("backport")) {
+ if (jsonFlagSet.has(BACKPORT_KEY)) {
for (Map.Entry<String, JsonElement> backport :
- jsonFlagSet.get("backport").getAsJsonObject().entrySet()) {
+ jsonFlagSet.get(BACKPORT_KEY).getAsJsonObject().entrySet()) {
configurationBuilder.putBackportCoreLibraryMember(
backport.getKey(), backport.getValue().getAsString());
}
}
- if (jsonFlagSet.has("emulate_interface")) {
+ if (jsonFlagSet.has(EMULATE_INTERFACE_KEY)) {
for (Map.Entry<String, JsonElement> itf :
- jsonFlagSet.get("emulate_interface").getAsJsonObject().entrySet()) {
+ jsonFlagSet.get(EMULATE_INTERFACE_KEY).getAsJsonObject().entrySet()) {
configurationBuilder.putEmulateLibraryInterface(itf.getKey(), itf.getValue().getAsString());
}
}
- if (jsonFlagSet.has("custom_conversion")) {
+ if (jsonFlagSet.has(CUSTOM_CONVERSION_KEY)) {
for (Map.Entry<String, JsonElement> conversion :
- jsonFlagSet.get("custom_conversion").getAsJsonObject().entrySet()) {
+ jsonFlagSet.get(CUSTOM_CONVERSION_KEY).getAsJsonObject().entrySet()) {
configurationBuilder.putCustomConversion(
conversion.getKey(), conversion.getValue().getAsString());
}
}
- if (jsonFlagSet.has("dont_rewrite")) {
- JsonArray dontRewrite = jsonFlagSet.get("dont_rewrite").getAsJsonArray();
+ 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());
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
index c911d6b..23a6465 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
@@ -112,7 +112,6 @@
private final Set<DexType> invalidWrappers = Sets.newConcurrentHashSet();
private final DexItemFactory factory;
private final DesugaredLibraryAPIConverter converter;
- private final DexString vivifiedSourceFile;
DesugaredLibraryWrapperSynthesizer(AppView<?> appView, DesugaredLibraryAPIConverter converter) {
this.appView = appView;
@@ -126,7 +125,6 @@
+ WRAPPER_PREFIX;
dexWrapperPrefixDexString = factory.createString(dexWrapperPrefixString);
this.converter = converter;
- this.vivifiedSourceFile = appView.dexItemFactory().createString("vivified");
}
boolean hasSynthesized(DexType type) {
@@ -134,96 +132,7 @@
}
boolean canGenerateWrapper(DexType type) {
- return typeWrappers.containsKey(type) || canGenerateWrapper(appView.definitionFor(type));
- }
-
- /**
- * Wrappers can/are generated for all types that appear to support wrapper generation. The
- * assumptions right now are that the 'type' must be specified in the bootclasspath library, that
- * means the library component for any build (all of D8, R8 *and* L8). For L8 the type lookup
- * needs to bypass the usual definitionsFor and look directly in the library on the initial app.
- *
- * <p>In addition (or rather before doing the lookup) the class to be wrapped must satisfy several
- * requirements: its super types can also be wrapped, it has no instance fields, all instance
- * methods are non-final and public and the types in them are also library defined. If all of that
- * holds wrappers for the type are created.
- *
- * <p>TODO(b/158645207): Consider adding wrappers to the spec and have this be precise.
- */
- boolean canGenerateWrapper(DexClass clazz) {
- if (typeWrappers.containsKey(clazz.type)) {
- return true;
- }
- if (clazz == null
- || clazz.accessFlags.isFinal()
- || !clazz.accessFlags.isPublic()
- || clazz.isAnonymousClass()) {
- return false;
- }
- if (!clazz.isLibraryClass()) {
- if (!appView.options().isDesugaredLibraryCompilation()) {
- return false;
- }
- // If doing the desugared library compilation, then the class to be wrapped must be defined on
- // the actual bootclasspath (not the desugared library). The definition lookup must thus
- // bypass the usual lookup in this case.
- if (!appView.options().desugaredLibraryBootclasspathDefinitions.test(clazz.type)) {
- return false;
- }
- }
- if (clazz.superType == null) {
- return false;
- }
- if (clazz.superType != factory.objectType) {
- if (!canGenerateWrapper(clazz.superType)) {
- return false;
- }
- }
- for (DexType iface : clazz.interfaces.values) {
- if (!canGenerateWrapper(iface)) {
- return false;
- }
- }
- for (DexEncodedField field : clazz.instanceFields()) {
- return false;
- }
- for (DexEncodedMethod method : clazz.virtualMethods()) {
- if (method.isFinal() || !method.isPublic()) {
- return false;
- }
- if (isUndefinedOrNonLibraryType(method.proto().returnType)) {
- return false;
- }
- for (DexType param : method.proto().parameters.values) {
- if (isUndefinedOrNonLibraryType(param)) {
- return false;
- }
- }
- }
- return true;
- }
-
- private boolean isUndefinedOrNonLibraryType(DexType type) {
- if (type.isPrimitiveType()) {
- return false;
- }
- DexType baseType = type.toBaseType(appView.dexItemFactory());
- if (!baseType.isClassType()) {
- return false;
- }
- DexClass clazz = appView.definitionFor(baseType);
- if (clazz == null) {
- return true;
- }
- if (clazz.isLibraryClass()) {
- return false;
- }
- if (!appView.options().isDesugaredLibraryCompilation()) {
- return true;
- }
- // The wrapper referenced type is also considered undefined if not on the bootclasspath.
- // TODO(b/158718959): Notice use of 'type' vs 'clazz.type' as emulated interfaces are mutated.
- return !appView.options().desugaredLibraryBootclasspathDefinitions.test(type);
+ return appView.options().desugaredLibraryConfiguration.getWrapperConversions().contains(type);
}
DexType getTypeWrapper(DexType type) {
@@ -248,7 +157,7 @@
return wrappers.computeIfAbsent(
type,
t -> {
- assert canGenerateWrapper(type);
+ assert canGenerateWrapper(type) : type;
DexType wrapperType = createWrapperType(type, suffix);
assert converter.canGenerateWrappersAndCallbacks()
|| appView.definitionFor(wrapperType).isClasspathClass()
@@ -544,7 +453,7 @@
true);
}
- void finalizeWrappersForD8(
+ void finalizeWrappersForL8(
DexApplication.Builder<?> builder, IRConverter irConverter, ExecutorService executorService)
throws ExecutionException {
List<DexProgramClass> synthesizedWrappers = synthesizeWrappers();
@@ -552,16 +461,13 @@
}
private List<DexProgramClass> synthesizeWrappers() {
+ DesugaredLibraryConfiguration conf = appView.options().desugaredLibraryConfiguration;
+ for (DexType type : conf.getWrapperConversions()) {
+ assert !conf.getCustomConversions().containsKey(type);
+ getTypeWrapper(type);
+ }
Map<DexType, DexClass> synthesizedWrappers = new IdentityHashMap<>();
List<DexProgramClass> additions = new ArrayList<>();
- DesugaredLibraryConfiguration conf = appView.options().desugaredLibraryConfiguration;
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- if (conf.getCustomConversions().get(clazz.type) == null
- && appView.rewritePrefix.hasRewrittenType(clazz.type, appView)
- && canGenerateWrapper(clazz)) {
- getTypeWrapper(clazz.type);
- }
- }
int total = typeWrappers.size() + vivifiedTypeWrappers.size();
generateWrappers(
ClassKind.PROGRAM,
@@ -587,7 +493,7 @@
});
}
- void generateWrappers(
+ private void generateWrappers(
ClassKind classKind,
Set<DexType> synthesized,
BiConsumer<DexType, DexClass> generatedCallback) {
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index f83359a..35e4614 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -680,9 +680,6 @@
public DesugaredLibraryConfiguration desugaredLibraryConfiguration =
DesugaredLibraryConfiguration.empty();
- // Method to lookup library definitions.
- public Predicate<DexType> desugaredLibraryBootclasspathDefinitions;
-
public boolean relocatorCompilation = false;
// If null, no keep rules are recorded.
diff --git a/src/test/java/com/android/tools/r8/DiagnosticsMatcher.java b/src/test/java/com/android/tools/r8/DiagnosticsMatcher.java
index 70518d7..a800801 100644
--- a/src/test/java/com/android/tools/r8/DiagnosticsMatcher.java
+++ b/src/test/java/com/android/tools/r8/DiagnosticsMatcher.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.ExceptionDiagnostic;
+import org.hamcrest.CoreMatchers;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
@@ -71,15 +72,20 @@
}
public static Matcher<Diagnostic> diagnosticPosition(Position position) {
+ return diagnosticPosition(CoreMatchers.equalTo(position));
+ }
+
+ public static Matcher<Diagnostic> diagnosticPosition(Matcher<Position> positionMatcher) {
return new DiagnosticsMatcher() {
@Override
protected boolean eval(Diagnostic diagnostic) {
- return diagnostic.getPosition().equals(position);
+ return positionMatcher.matches(diagnostic.getPosition());
}
@Override
protected void explain(Description description) {
- description.appendText("position ").appendText(position.getDescription());
+ description.appendText("position ");
+ positionMatcher.describeTo(description);
}
};
}
diff --git a/src/test/java/com/android/tools/r8/PositionMatcher.java b/src/test/java/com/android/tools/r8/PositionMatcher.java
new file mode 100644
index 0000000..4fca1c6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/PositionMatcher.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2020, 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;
+
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.position.TextPosition;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+public abstract class PositionMatcher extends TypeSafeMatcher<Position> {
+
+ public static Matcher<Position> positionLine(int line) {
+ return new PositionMatcher() {
+ @Override
+ protected boolean matchesSafely(Position position) {
+ return position instanceof TextPosition && ((TextPosition) position).getLine() == line;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("with line " + line);
+ }
+ };
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java
new file mode 100644
index 0000000..c7d0c92
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java
@@ -0,0 +1,314 @@
+// Copyright (c) 2020, 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.desugar.desugaredlibrary;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticOrigin;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.NoneRuntime;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AbortException;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class DesugaredLibraryConfigurationParsingTest extends TestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public DesugaredLibraryConfigurationParsingTest(TestParameters parameters) {
+ assertEquals(NoneRuntime.getInstance(), parameters.getRuntime());
+ }
+
+ final AndroidApiLevel minApi = AndroidApiLevel.B;
+ final boolean libraryCompilation = true;
+
+ final DexItemFactory factory = new DexItemFactory();
+ final Origin origin =
+ new Origin(Origin.root()) {
+ @Override
+ public String part() {
+ return "Test Origin";
+ }
+ };
+
+ final Map<String, Object> TEMPLATE =
+ ImmutableMap.<String, Object>builder()
+ .put(
+ "configuration_format_version",
+ DesugaredLibraryConfigurationParser.MAX_SUPPORTED_VERSION)
+ .put("group_id", "com.tools.android")
+ .put("artifact_id", "desugar_jdk_libs")
+ .put("version", "0.0.0")
+ .put("required_compilation_api_level", 1)
+ .put("synthesized_library_classes_package_prefix", "j$.")
+ .put("common_flags", Collections.emptyList())
+ .put("program_flags", Collections.emptyList())
+ .put("library_flags", Collections.emptyList())
+ .build();
+
+ private LinkedHashMap<String, Object> template() {
+ return new LinkedHashMap<>(TEMPLATE);
+ }
+
+ private DesugaredLibraryConfigurationParser parser(DiagnosticsHandler handler) {
+ return new DesugaredLibraryConfigurationParser(
+ factory, new Reporter(handler), libraryCompilation, minApi.getLevel());
+ }
+
+ private DesugaredLibraryConfiguration runPassing(String resource) {
+ return runPassing(StringResource.fromString(resource, origin));
+ }
+
+ private DesugaredLibraryConfiguration runPassing(StringResource resource) {
+ TestDiagnosticMessagesImpl handler = new TestDiagnosticMessagesImpl();
+ DesugaredLibraryConfiguration config = parser(handler).parse(resource);
+ handler.assertNoMessages();
+ return config;
+ }
+
+ private void runFailing(String json, Consumer<TestDiagnosticMessages> checker) {
+ TestDiagnosticMessagesImpl handler = new TestDiagnosticMessagesImpl();
+ try {
+ parser(handler).parse(StringResource.fromString(json, origin));
+ fail("Expected failure");
+ } catch (AbortException e) {
+ checker.accept(handler);
+ }
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ // Just test that the reference file parses without issues.
+ DesugaredLibraryConfiguration config =
+ runPassing(StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING));
+ assertEquals(libraryCompilation, config.isLibraryCompilation());
+ }
+
+ @Test
+ public void testEmpty() {
+ runFailing(
+ "",
+ diagnostics -> {
+ diagnostics.assertErrorsMatch(
+ allOf(
+ diagnosticMessage(containsString("Not a JSON Object")),
+ diagnosticOrigin(origin)));
+ });
+ }
+
+ @Test
+ public void testRequiredKeys() {
+ ImmutableList<String> requiredKeys =
+ ImmutableList.of(
+ "configuration_format_version",
+ "group_id",
+ "artifact_id",
+ "version",
+ "required_compilation_api_level",
+ "synthesized_library_classes_package_prefix",
+ "common_flags",
+ "program_flags",
+ "library_flags");
+ for (String key : requiredKeys) {
+ Map<String, Object> data = template();
+ data.remove(key);
+ runFailing(
+ toJson(data),
+ diagnostics ->
+ diagnostics.assertErrorsMatch(
+ allOf(
+ diagnosticMessage(containsString("Invalid desugared library configuration")),
+ diagnosticMessage(containsString("Expected required key '" + key + "'")),
+ diagnosticOrigin(origin))));
+ }
+ }
+
+ @Test
+ public void testUnsupportedBelow() {
+ LinkedHashMap<String, Object> data = template();
+ data.put("configuration_format_version", 4);
+ runFailing(
+ toJson(data),
+ diagnostics ->
+ diagnostics.assertErrorsMatch(
+ allOf(
+ diagnosticMessage(containsString("upgrade the configuration file")),
+ diagnosticOrigin(origin))));
+ }
+
+ @Test
+ public void testUnsupportedAbove() {
+ LinkedHashMap<String, Object> data = template();
+ data.put("configuration_format_version", 100000);
+ runFailing(
+ toJson(data),
+ diagnostics ->
+ diagnostics.assertErrorsMatch(
+ allOf(
+ diagnosticMessage(containsString("upgrade the D8/R8 compiler")),
+ diagnosticOrigin(origin))));
+ }
+
+ @Test
+ public void testCustomAndWrapperOverlap() {
+ LinkedHashMap<String, Object> data = template();
+ data.put(
+ "common_flags",
+ ImmutableList.of(
+ ImmutableMap.of(
+ "api_level_below_or_equal",
+ 100000,
+ "custom_conversion",
+ ImmutableMap.of("java.util.Foo", "j$.util.FooConv"),
+ "wrapper_conversion",
+ ImmutableList.of("java.util.Foo"))));
+ runFailing(
+ toJson(data),
+ diagnostics ->
+ diagnostics.assertErrorsMatch(
+ allOf(
+ diagnosticMessage(containsString("Duplicate types")),
+ diagnosticMessage(containsString("java.util.Foo")),
+ diagnosticOrigin(origin))));
+ }
+
+ @Test
+ public void testRedefinition() {
+ LinkedHashMap<String, Object> data = template();
+ data.put(
+ "common_flags",
+ ImmutableList.of(
+ ImmutableMap.of(
+ "api_level_below_or_equal",
+ 100000,
+ "custom_conversion",
+ ImmutableMap.of("java.util.Foo", "j$.util.FooConv1"))));
+ data.put(
+ "library_flags",
+ ImmutableList.of(
+ ImmutableMap.of(
+ "api_level_below_or_equal",
+ 100000,
+ "custom_conversion",
+ ImmutableMap.of("java.util.Foo", "j$.util.FooConv2"))));
+ runFailing(
+ toJson(data),
+ diagnostics ->
+ diagnostics.assertErrorsMatch(
+ allOf(
+ diagnosticMessage(containsString("Duplicate assignment of key")),
+ diagnosticMessage(containsString("java.util.Foo")),
+ diagnosticMessage(containsString("custom_conversion")),
+ diagnosticOrigin(origin))));
+ }
+
+ @Test
+ public void testDuplicate() {
+ LinkedHashMap<String, Object> data = template();
+ data.put(
+ "common_flags",
+ ImmutableList.of(
+ ImmutableMap.of(
+ "api_level_below_or_equal",
+ 100000,
+ "custom_conversion",
+ ImmutableMap.of(
+ "java.util.Foo", "j$.util.FooConv1",
+ "java.util.Foo2", "j$.util.FooConv2"))));
+ // The gson parser will overwrite the key in order during parsing, thus hiding potential issues.
+ DesugaredLibraryConfiguration config = runPassing(toJson(data).replace("Foo2", "Foo"));
+ assertEquals(
+ Collections.singletonList("java.util.Foo"),
+ config.getCustomConversions().keySet().stream()
+ .map(DexType::toString)
+ .collect(Collectors.toList()));
+ assertEquals(
+ Collections.singletonList("j$.util.FooConv2"),
+ config.getCustomConversions().values().stream()
+ .map(DexType::toString)
+ .collect(Collectors.toList()));
+ }
+
+ // JSON building helpers.
+ // This does not use gson to make the text input construction independent of gson.
+
+ private static String toJson(Map<String, Object> data) {
+ StringBuilder builder = new StringBuilder();
+ toJsonObject(data, builder);
+ return builder.toString();
+ }
+
+ private static void toJsonObject(Map<String, Object> data, StringBuilder builder) {
+ StringUtils.append(
+ builder,
+ ListUtils.map(
+ data.entrySet(),
+ entry -> "\n " + quote(entry.getKey()) + ": " + toJsonElement(entry.getValue())),
+ ", ",
+ BraceType.TUBORG);
+ }
+
+ private static String toJsonElement(Object element) {
+ StringBuilder builder = new StringBuilder();
+ toJsonElement(element, builder);
+ return builder.toString();
+ }
+
+ private static void toJsonElement(Object element, StringBuilder builder) {
+ if (element instanceof String) {
+ builder.append(quote((String) element));
+ } else if (element instanceof Integer) {
+ builder.append(element);
+ } else if (element instanceof List) {
+ toJsonList((List<Object>) element, builder);
+ } else if (element instanceof Map) {
+ toJsonObject((Map<String, Object>) element, builder);
+ } else {
+ throw new IllegalStateException("Unexpected object type: " + element.getClass());
+ }
+ }
+
+ private static void toJsonList(List<Object> element, StringBuilder builder) {
+ StringUtils.append(
+ builder, ListUtils.map(element, o -> "\n " + toJsonElement(o)), ", ", BraceType.SQUARE);
+ }
+
+ private static String quote(String str) {
+ return "\"" + str + "\"";
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
new file mode 100644
index 0000000..846b65d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
@@ -0,0 +1,304 @@
+// Copyright (c) 2020, 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.desugar.desugaredlibrary;
+
+import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.GenerateLintFiles;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.NoneRuntime;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+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.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.WorkList;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ExtractWrapperTypesTest extends TestBase {
+
+ // Filter on types that do not need to be considered for wrapping.
+ private static boolean doesNotNeedWrapper(String type, Set<String> customConversions) {
+ return excludePackage(type)
+ || NOT_NEEDED_NOT_IN_DOCS.contains(type)
+ || FINAL_CLASSES.contains(type)
+ || customConversions.contains(type);
+ }
+
+ private static boolean excludePackage(String type) {
+ return type.startsWith("java.lang.")
+ || type.startsWith("java.nio.")
+ || type.startsWith("java.security.")
+ || type.startsWith("java.net.")
+ || type.startsWith("java.awt.")
+ || type.startsWith("java.util.concurrent.");
+ }
+
+ // Types not picked up by the android.jar scan but for which wrappers are needed.
+ private static final Set<String> ADDITIONAL_WRAPPERS = ImmutableSet.of();
+
+ // Types not in API docs, referenced in android.jar and must be wrapped.
+ private static final Set<String> NEEDED_BUT_NOT_IN_DOCS = ImmutableSet.of();
+
+ // Types not in API docs, referenced in android.jar but need not be wrapped.
+ private static final Set<String> NOT_NEEDED_NOT_IN_DOCS =
+ ImmutableSet.of(
+ "java.util.Base64$Decoder",
+ "java.util.Base64$Encoder",
+ "java.util.Calendar$Builder",
+ "java.util.Locale$Builder",
+ "java.util.Locale$Category",
+ "java.util.Locale$FilteringMode",
+ "java.util.SplittableRandom");
+
+ // List of referenced final classes (cannot be wrapper converted) with no custom conversions.
+ private static final Set<String> FINAL_CLASSES =
+ ImmutableSet.of(
+ // TODO(b/159304624): Does this need custom conversion?
+ "java.time.Period");
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ // TODO: parameterize to check both api<=23 as well as 23<api<26 for which the spec differs.
+ private final AndroidApiLevel minApi = AndroidApiLevel.B;
+ private final AndroidApiLevel targetApi = AndroidApiLevel.Q;
+
+ public ExtractWrapperTypesTest(TestParameters parameters) {
+ assertEquals(NoneRuntime.getInstance(), parameters.getRuntime());
+ }
+
+ @Test
+ public void checkConsistency() {
+ List<Set<String>> sets =
+ ImmutableList.of(
+ ADDITIONAL_WRAPPERS, NEEDED_BUT_NOT_IN_DOCS, NOT_NEEDED_NOT_IN_DOCS, FINAL_CLASSES);
+ for (Set<String> set1 : sets) {
+ for (Set<String> set2 : sets) {
+ if (set1 != set2) {
+ assertEquals(Collections.emptySet(), Sets.intersection(set1, set2));
+ }
+ }
+ }
+ for (Set<String> set : sets) {
+ for (String type : set) {
+ assertFalse(excludePackage(type));
+ }
+ }
+ }
+
+ @Test
+ public void test() throws Exception {
+ CodeInspector desugaredApiJar = getDesugaredApiJar();
+ Set<ClassReference> preDesugarTypes = getPreDesugarTypes();
+
+ DesugaredLibraryConfiguration conf = getDesugaredLibraryConfiguration();
+ Set<String> wrappersInSpec =
+ conf.getWrapperConversions().stream().map(DexType::toString).collect(Collectors.toSet());
+ Set<String> customConversionsInSpec =
+ conf.getCustomConversions().keySet().stream()
+ .map(DexType::toString)
+ .collect(Collectors.toSet());
+ assertEquals(
+ Collections.emptySet(), Sets.intersection(wrappersInSpec, customConversionsInSpec));
+ assertEquals(Collections.emptySet(), Sets.intersection(FINAL_CLASSES, customConversionsInSpec));
+
+ CodeInspector nonDesugaredJar = new CodeInspector(ToolHelper.getAndroidJar(targetApi));
+ Map<ClassReference, Set<MethodReference>> directWrappers =
+ getDirectlyReferencedWrapperTypes(
+ desugaredApiJar, preDesugarTypes, nonDesugaredJar, customConversionsInSpec);
+ Map<ClassReference, Set<ClassReference>> indirectWrappers =
+ getIndirectlyReferencedWrapperTypes(
+ directWrappers, preDesugarTypes, nonDesugaredJar, customConversionsInSpec);
+
+ {
+ Set<String> missingWrappers = getMissingWrappers(directWrappers, wrappersInSpec);
+ assertTrue(
+ "Missing direct wrappers:\n" + String.join("\n", missingWrappers),
+ missingWrappers.isEmpty());
+ }
+
+ {
+ Set<String> missingWrappers = getMissingWrappers(indirectWrappers, wrappersInSpec);
+ assertTrue(
+ "Missing indirect wrappers:\n" + String.join("\n", missingWrappers),
+ missingWrappers.isEmpty());
+ }
+
+ Set<String> additionalWrappers = new TreeSet<>();
+ for (String wrapper : wrappersInSpec) {
+ ClassReference item = Reference.classFromTypeName(wrapper);
+ if (!directWrappers.containsKey(item)
+ && !indirectWrappers.containsKey(item)
+ && !ADDITIONAL_WRAPPERS.contains(wrapper)) {
+ additionalWrappers.add(wrapper);
+ }
+ }
+ assertTrue(
+ "Additional wrapper:\n" + String.join("\n", additionalWrappers),
+ additionalWrappers.isEmpty());
+
+ assertEquals(
+ directWrappers.size() + indirectWrappers.size() + ADDITIONAL_WRAPPERS.size(),
+ wrappersInSpec.size());
+ }
+
+ private static <T> Set<String> getMissingWrappers(
+ Map<ClassReference, Set<T>> expected, Set<String> wrappersInSpec) {
+ Set<String> missingWrappers = new TreeSet<>();
+ for (ClassReference addition : expected.keySet()) {
+ String item = descriptorToJavaType(addition.getDescriptor());
+ if (!wrappersInSpec.contains(item)) {
+ missingWrappers.add(item + " referenced from: " + expected.get(addition));
+ }
+ }
+ return missingWrappers;
+ }
+
+ private DesugaredLibraryConfiguration getDesugaredLibraryConfiguration() {
+ DesugaredLibraryConfigurationParser parser =
+ new DesugaredLibraryConfigurationParser(
+ new DexItemFactory(), null, true, minApi.getLevel());
+ return parser.parse(StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING));
+ }
+
+ private Map<ClassReference, Set<MethodReference>> getDirectlyReferencedWrapperTypes(
+ CodeInspector desugaredApiJar,
+ Set<ClassReference> preDesugarTypes,
+ CodeInspector nonDesugaredJar,
+ Set<String> customConversions) {
+ Map<ClassReference, Set<MethodReference>> directWrappers = new HashMap<>();
+ nonDesugaredJar.forAllClasses(
+ clazz -> {
+ clazz.forAllMethods(
+ method -> {
+ if (!method.isPublic() && !method.isProtected()) {
+ return;
+ }
+ if (desugaredApiJar.method(method.asMethodReference()).isPresent()) {
+ return;
+ }
+ Consumer<ClassReference> adder =
+ t ->
+ directWrappers
+ .computeIfAbsent(t, k -> new HashSet<>())
+ .add(method.asMethodReference());
+ MethodSignature signature = method.getFinalSignature().asMethodSignature();
+ addType(adder, signature.type, preDesugarTypes, customConversions);
+ for (String parameter : signature.parameters) {
+ addType(adder, parameter, preDesugarTypes, customConversions);
+ }
+ });
+ });
+ return directWrappers;
+ }
+
+ private Map<ClassReference, Set<ClassReference>> getIndirectlyReferencedWrapperTypes(
+ Map<ClassReference, Set<MethodReference>> directWrappers,
+ Set<ClassReference> existing,
+ CodeInspector latest,
+ Set<String> customConversions) {
+ Map<ClassReference, Set<ClassReference>> indirectWrappers = new HashMap<>();
+ WorkList<ClassReference> worklist = WorkList.newEqualityWorkList(directWrappers.keySet());
+ while (worklist.hasNext()) {
+ ClassReference reference = worklist.next();
+ ClassSubject clazz = latest.clazz(reference);
+ clazz.forAllVirtualMethods(
+ method -> {
+ assertTrue(method.toString(), method.isPublic() || method.isProtected());
+ MethodSignature signature = method.getFinalSignature().asMethodSignature();
+ Consumer<ClassReference> adder =
+ t -> {
+ if (worklist.addIfNotSeen(t)) {
+ indirectWrappers.computeIfAbsent(t, k -> new HashSet<>()).add(reference);
+ }
+ };
+ addType(adder, signature.type, existing, customConversions);
+ for (String parameter : signature.parameters) {
+ addType(adder, parameter, existing, customConversions);
+ }
+ });
+ }
+ return indirectWrappers;
+ }
+
+ private Set<ClassReference> getPreDesugarTypes() throws IOException {
+ Set<ClassReference> existing = new HashSet<>();
+ Path androidJar = ToolHelper.getAndroidJar(minApi);
+ new CodeInspector(androidJar)
+ .forAllClasses(
+ clazz -> {
+ if (clazz.getFinalName().startsWith("java.")) {
+ existing.add(Reference.classFromTypeName(clazz.getFinalName()));
+ }
+ });
+ return existing;
+ }
+
+ private CodeInspector getDesugaredApiJar() throws Exception {
+ Path out = temp.newFolder().toPath();
+ GenerateLintFiles desugaredApi =
+ new GenerateLintFiles(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING.toString(), out.toString());
+ desugaredApi.run(targetApi.getLevel());
+ return new CodeInspector(
+ out.resolve("compile_api_level_" + targetApi.getLevel())
+ .resolve("desugared_apis_" + targetApi.getLevel() + "_" + minApi.getLevel() + ".jar"));
+ }
+
+ private void addType(
+ Consumer<ClassReference> additions,
+ String type,
+ Set<ClassReference> preDesugarTypes,
+ Set<String> customConversions) {
+ if (type.equals("void")) {
+ return;
+ }
+ TypeReference typeReference = Reference.typeFromTypeName(type);
+ if (typeReference.isArray()) {
+ typeReference = typeReference.asArray().getBaseType();
+ }
+ if (typeReference.isClass()) {
+ ClassReference clazz = typeReference.asClass();
+ String clazzType = descriptorToJavaType(clazz.getDescriptor());
+ if (clazzType.startsWith("java.")
+ && !doesNotNeedWrapper(clazzType, customConversions)
+ && !preDesugarTypes.contains(clazz)) {
+ additions.accept(clazz);
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java
index 106dd72..e8aa581 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java
@@ -20,6 +20,7 @@
import java.util.function.IntSupplier;
import java.util.function.LongConsumer;
import java.util.function.LongSupplier;
+import java.util.function.Supplier;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -67,6 +68,7 @@
.addLibraryClasses(CustomLibClass.class)
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
+ .assertNoMessages()
.addDesugaredCoreLibraryRunClassPath(
this::buildDesugaredLibrary,
parameters.getApiLevel(),
@@ -106,16 +108,16 @@
BiFunction<String, String, Character> biFunction =
CustomLibClass.mixBiFunctions((String i, String j) -> i + j, (String s) -> s.charAt(1));
System.out.println(biFunction.apply("1", "2"));
- BooleanSupplier booleanSupplier = CustomLibClass.mixBoolSuppliers(() -> true, () -> false);
+ BooleanSupplier booleanSupplier =
+ () -> CustomLibClass.mixBoolSuppliers(() -> true, () -> false).get();
System.out.println(booleanSupplier.getAsBoolean());
LongConsumer longConsumer = CustomLibClass.mixLong(() -> 1L, System.out::println);
longConsumer.accept(2L);
DoublePredicate doublePredicate =
CustomLibClass.mixPredicate(d -> d > 1.0, d -> d == 2.0, d -> d < 3.0);
System.out.println(doublePredicate.test(2.0));
- // Reverse wrapper should not exist.
System.out.println(CustomLibClass.extractInt(() -> 5));
- System.out.println(CustomLibClass.getDoubleSupplier().getAsDouble());
+ System.out.println(CustomLibClass.getDoubleSupplier().get());
}
static class Object1 {}
@@ -163,13 +165,18 @@
return operator.andThen(function);
}
- public static BooleanSupplier mixBoolSuppliers(
- BooleanSupplier supplier1, BooleanSupplier supplier2) {
- return () -> supplier1.getAsBoolean() && supplier2.getAsBoolean();
+ // BooleanSupplier is not a wrapped type, so it can't be placed on the boundary.
+ public static Supplier<Boolean> mixBoolSuppliers(
+ Supplier<Boolean> supplier1, Supplier<Boolean> supplier2) {
+ BooleanSupplier wrap1 = supplier1::get;
+ BooleanSupplier wrap2 = supplier2::get;
+ return () -> wrap1.getAsBoolean() && wrap2.getAsBoolean();
}
- public static LongConsumer mixLong(LongSupplier supplier, LongConsumer consumer) {
- return l -> consumer.accept(l + supplier.getAsLong());
+ // LongSupplier is not a wrapped type, so it can't be placed on the boundary.
+ public static LongConsumer mixLong(Supplier<Long> supplier, LongConsumer consumer) {
+ LongSupplier wrap = supplier::get;
+ return l -> consumer.accept(l + wrap.getAsLong());
}
public static DoublePredicate mixPredicate(
@@ -177,12 +184,16 @@
return predicate1.and(predicate2).and(predicate3);
}
- public static int extractInt(IntSupplier supplier) {
- return supplier.getAsInt();
+ // IntSupplier is not a wrapped type, so it can't be placed on the boundary.
+ public static int extractInt(Supplier<Integer> supplier) {
+ IntSupplier wrap = supplier::get;
+ return wrap.getAsInt();
}
- public static DoubleSupplier getDoubleSupplier() {
- return () -> 42.0;
+ // DoubleSupplier is not a wrapped type, so it can't be placed on the boundary.
+ public static Supplier<Double> getDoubleSupplier() {
+ DoubleSupplier supplier = () -> 42.0;
+ return supplier::getAsDouble;
}
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperPlacementTest.java
similarity index 93%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeTest.java
rename to src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperPlacementTest.java
index c95420c..cbab10c 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperMergeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/WrapperPlacementTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.desugar.desugaredlibrary.conversiontests;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.TestParameters;
@@ -26,7 +27,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class WrapperMergeTest extends DesugaredLibraryTestBase {
+public class WrapperPlacementTest extends DesugaredLibraryTestBase {
private static final String EXPECTED = StringUtils.lines("[1, 2, 3]", "[2, 3, 4]");
@@ -37,7 +38,7 @@
private final TestParameters parameters;
- public WrapperMergeTest(TestParameters parameters) {
+ public WrapperPlacementTest(TestParameters parameters) {
this.parameters = parameters;
}
@@ -96,11 +97,8 @@
private void assertCoreLibContainsWrappers(Path coreLib) throws IOException {
CodeInspector inspector = new CodeInspector(coreLib);
- // TODO(b/158645207): Consider having wrappers in the spec and avoiding this issue.
- assertEquals(
- "Number of generated wrappers changes. Manually verify validity of the change.",
- 144,
- getWrappers(inspector).count());
+ Stream<FoundClassSubject> wrappers = getWrappers(inspector);
+ assertNotEquals(0, wrappers.count());
}
private void assertNoWrappers(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java b/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
index 1363bbd..c81a2d6 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
@@ -75,12 +75,10 @@
com.android.tools.r8.memberrebinding.b135627418.runtime.InsetDrawable.class)
.setMinApi(parameters.getRuntime())
.addOptionsModification(
- options -> {
- options.desugaredLibraryConfiguration =
- DesugaredLibraryConfiguration.withOnlyRewritePrefixForTesting(
- ImmutableMap.of(packageName + ".runtime", packageName + ".library"));
- options.desugaredLibraryBootclasspathDefinitions = type -> false;
- })
+ options ->
+ options.desugaredLibraryConfiguration =
+ DesugaredLibraryConfiguration.withOnlyRewritePrefixForTesting(
+ ImmutableMap.of(packageName + ".runtime", packageName + ".library")))
.compile();
testForR8(parameters.getBackend())