Version 2.0.51
Cherry pick: Desugared lib wrappers
CL: https://r8-review.googlesource.com/c/r8/+/48100
Hash: 6e13f62cf493197c4760f7472f71fc5bfa8cc525
Change-Id: I88c304aa5c426e24a9b8fc4dc3020bde5d28e4e3
Bug: 151207121
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 7d55176..909219f 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "2.0.50";
+ public static final String LABEL = "2.0.51";
private Version() {
}
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 4483eca..d45a68c 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
@@ -81,6 +81,10 @@
}
}
+ public static boolean isVivifiedType(DexType type) {
+ return type.descriptor.toString().startsWith("L" + VIVIFIED_PREFIX);
+ }
+
public void desugar(IRCode code) {
if (wrapperSynthesizor.hasSynthesized(code.method.method.holder)) {
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 bf48a16..99994fe 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
@@ -19,6 +19,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
+import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.FieldAccessFlags;
@@ -31,8 +32,6 @@
import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterWrapperCfCodeProvider;
import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterWrapperConversionCfCodeProvider;
import com.android.tools.r8.origin.SynthesizedOrigin;
-import com.android.tools.r8.utils.Box;
-import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.Sets;
import java.util.ArrayList;
@@ -45,8 +44,6 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
// I am responsible for the generation of wrappers used to call library APIs when desugaring
// libraries. Wrappers can be both ways, wrapping the desugarType as a type, or the type as
@@ -98,37 +95,34 @@
public static final String VIVIFIED_TYPE_WRAPPER_SUFFIX = "$-V-WRP";
private final AppView<?> appView;
- private final Map<DexType, Pair<DexType, DexProgramClass>> typeWrappers =
- new ConcurrentHashMap<>();
- private final Map<DexType, Pair<DexType, DexProgramClass>> vivifiedTypeWrappers =
- new ConcurrentHashMap<>();
+ private final DexString dexWrapperPrefix;
+ private final Map<DexType, DexType> typeWrappers = new ConcurrentHashMap<>();
+ private final Map<DexType, DexType> vivifiedTypeWrappers = new ConcurrentHashMap<>();
// The invalidWrappers are wrappers with incorrect behavior because of final methods that could
// not be overridden. Such wrappers are awful because the runtime behavior is undefined and does
// not raise explicit errors. So we register them here and conversion methods for such wrappers
// raise a runtime exception instead of generating the wrapper.
private final Set<DexType> invalidWrappers = Sets.newConcurrentHashSet();
- private final Set<DexType> generatedWrappers = Sets.newConcurrentHashSet();
private final DexItemFactory factory;
private final DesugaredLibraryAPIConverter converter;
DesugaredLibraryWrapperSynthesizer(AppView<?> appView, DesugaredLibraryAPIConverter converter) {
this.appView = appView;
this.factory = appView.dexItemFactory();
+ this.dexWrapperPrefix = factory.createString("L" + WRAPPER_PREFIX);
this.converter = converter;
}
- public static boolean isSynthesizedWrapper(DexType clazz) {
- return clazz.descriptor.toString().contains(WRAPPER_PREFIX);
+ public static boolean isSynthesizedWrapper(DexType type) {
+ // Slow path, but more convenient since no instance is needed. Use hasSynthesized(DexType) when
+ // possible.
+ return type.descriptor.toString().startsWith("L" + WRAPPER_PREFIX);
}
boolean hasSynthesized(DexType type) {
- return generatedWrappers.contains(type);
+ return type.descriptor.startsWith(dexWrapperPrefix);
}
- // Wrapper initial generation section.
- // 1. Generate wrappers without conversion methods.
- // 2. Compute wrapper types.
-
boolean canGenerateWrapper(DexType type) {
DexClass dexClass = appView.definitionFor(type);
if (dexClass == null) {
@@ -138,15 +132,11 @@
}
DexType getTypeWrapper(DexType type) {
- return getWrapper(type, TYPE_WRAPPER_SUFFIX, typeWrappers, this::generateTypeWrapper);
+ return getWrapper(type, TYPE_WRAPPER_SUFFIX, typeWrappers);
}
DexType getVivifiedTypeWrapper(DexType type) {
- return getWrapper(
- type,
- VIVIFIED_TYPE_WRAPPER_SUFFIX,
- vivifiedTypeWrappers,
- this::generateVivifiedTypeWrapper);
+ return getWrapper(type, VIVIFIED_TYPE_WRAPPER_SUFFIX, vivifiedTypeWrappers);
}
private DexType createWrapperType(DexType type, String suffix) {
@@ -164,46 +154,27 @@
+ ";");
}
- private DexType getWrapper(
- DexType type,
- String suffix,
- Map<DexType, Pair<DexType, DexProgramClass>> wrappers,
- BiFunction<DexClass, DexType, DexProgramClass> wrapperGenerator) {
- // Answers the DexType of the wrapper. Generate the wrapper DexProgramClass if not already done,
- // except the conversions methods. Conversion method generation is postponed to know if the
- // reverse wrapper is present at generation time.
- // We generate the type while locking the concurrent hash map, but we release the lock before
- // generating the actual class to avoid locking for too long (hence the Pair).
+ private DexType getWrapper(DexType type, String suffix, Map<DexType, DexType> wrappers) {
assert !type.toString().startsWith(DesugaredLibraryAPIConverter.VIVIFIED_PREFIX);
- Box<Boolean> toGenerate = new Box<>(false);
- Pair<DexType, DexProgramClass> pair =
- wrappers.computeIfAbsent(
- type,
- t -> {
- toGenerate.set(true);
- DexType wrapperType = createWrapperType(type, suffix);
- generatedWrappers.add(wrapperType);
- return new Pair<>(wrapperType, null);
- });
- if (toGenerate.get()) {
- assert pair.getSecond() == null;
- DexClass dexClass = appView.definitionFor(type);
- // The dexClass should be a library class, so it cannot be null.
- assert dexClass != null
- && (dexClass.isLibraryClass() || appView.options().isDesugaredLibraryCompilation());
- if (dexClass.accessFlags.isFinal()) {
- throw appView
- .options()
- .reporter
- .fatalError(
- new StringDiagnostic(
- "Cannot generate a wrapper for final class "
- + dexClass.type
- + ". Add a custom conversion in the desugared library."));
- }
- pair.setSecond(wrapperGenerator.apply(dexClass, pair.getFirst()));
+ return wrappers.computeIfAbsent(type, t -> createWrapperType(type, suffix));
+ }
+
+ private DexClass getValidClassToWrap(DexType type) {
+ DexClass dexClass = appView.definitionFor(type);
+ // The dexClass should be a library class, so it cannot be null.
+ assert dexClass != null
+ && (dexClass.isLibraryClass() || appView.options().isDesugaredLibraryCompilation());
+ if (dexClass.accessFlags.isFinal()) {
+ throw appView
+ .options()
+ .reporter
+ .fatalError(
+ new StringDiagnostic(
+ "Cannot generate a wrapper for final class "
+ + dexClass.type
+ + ". Add a custom conversion in the desugared library."));
}
- return pair.getFirst();
+ return dexClass;
}
private DexType vivifiedTypeFor(DexType type) {
@@ -212,11 +183,12 @@
private DexProgramClass generateTypeWrapper(DexClass dexClass, DexType typeWrapperType) {
DexType type = dexClass.type;
- DexEncodedField wrapperField = synthesizeWrappedValueField(typeWrapperType, type);
+ DexEncodedField wrapperField = synthesizeWrappedValueEncodedField(typeWrapperType, type);
return synthesizeWrapper(
vivifiedTypeFor(type),
dexClass,
synthesizeVirtualMethodsForTypeWrapper(dexClass, wrapperField),
+ generateTypeConversion(type, typeWrapperType),
wrapperField);
}
@@ -224,11 +196,12 @@
DexClass dexClass, DexType vivifiedTypeWrapperType) {
DexType type = dexClass.type;
DexEncodedField wrapperField =
- synthesizeWrappedValueField(vivifiedTypeWrapperType, vivifiedTypeFor(type));
+ synthesizeWrappedValueEncodedField(vivifiedTypeWrapperType, vivifiedTypeFor(type));
return synthesizeWrapper(
type,
dexClass,
synthesizeVirtualMethodsForVivifiedTypeWrapper(dexClass, wrapperField),
+ generateVivifiedTypeConversion(type, vivifiedTypeWrapperType),
wrapperField);
}
@@ -236,6 +209,7 @@
DexType wrappingType,
DexClass clazz,
DexEncodedMethod[] virtualMethods,
+ DexEncodedMethod conversionMethod,
DexEncodedField wrapperField) {
boolean isItf = clazz.isInterface();
DexType superType = isItf ? factory.objectType : wrappingType;
@@ -257,9 +231,7 @@
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY, // No static fields.
new DexEncodedField[] {wrapperField},
- new DexEncodedMethod[] {
- synthesizeConstructor(wrapperField.field)
- }, // Conversions methods will be added later.
+ new DexEncodedMethod[] {synthesizeConstructor(wrapperField.field), conversionMethod},
virtualMethods,
factory.getSkipNameValidationForTesting(),
getChecksumSupplier(this, clazz.type),
@@ -446,8 +418,12 @@
return implementedMethods;
}
- private DexEncodedField synthesizeWrappedValueField(DexType holder, DexType fieldType) {
- DexField field = factory.createField(holder, fieldType, factory.wrapperFieldName);
+ private DexField wrappedValueField(DexType holder, DexType fieldType) {
+ return factory.createField(holder, fieldType, factory.wrapperFieldName);
+ }
+
+ private DexEncodedField synthesizeWrappedValueEncodedField(DexType holder, DexType fieldType) {
+ DexField field = wrappedValueField(holder, fieldType);
// Field is package private to be accessible from convert methods without a getter.
FieldAccessFlags fieldAccessFlags =
FieldAccessFlags.fromCfAccessFlags(Constants.ACC_FINAL | Constants.ACC_SYNTHETIC);
@@ -479,86 +455,70 @@
true);
}
- // Wrapper finalization section.
- // 1. Generate conversions methods (convert(type)).
- // 2. Add the synthesized classes.
- // 3. Process all methods.
-
void finalizeWrappers(
DexApplication.Builder<?> builder, IRConverter irConverter, ExecutorService executorService)
throws ExecutionException {
- assert verifyAllClassesGenerated();
- // We register first, then optimize to avoid warnings due to missing parameter types.
- registerWrappers(builder, typeWrappers);
- registerWrappers(builder, vivifiedTypeWrappers);
- finalizeWrappers(irConverter, executorService, typeWrappers, this::generateTypeConversions);
- finalizeWrappers(
- irConverter,
- executorService,
- vivifiedTypeWrappers,
- this::generateVivifiedTypeConversions);
+ List<DexProgramClass> synthesizedWrappers = generateWrappers();
+ registerAndProcessWrappers(builder, irConverter, executorService, synthesizedWrappers);
}
- private void registerWrappers(
- DexApplication.Builder<?> builder, Map<DexType, Pair<DexType, DexProgramClass>> wrappers) {
- for (DexType type : wrappers.keySet()) {
- DexProgramClass pgrmClass = wrappers.get(type).getSecond();
- assert pgrmClass != null;
- registerSynthesizedClass(pgrmClass, builder);
+ private List<DexProgramClass> generateWrappers() {
+ List<DexProgramClass> synthesizedWrappers = new ArrayList<>();
+ Set<DexType> synthesizedWrapperTypes = Sets.newIdentityHashSet();
+ // Generating a wrapper may require other wrappers to be generated, iterate until fix point.
+ while (synthesizedWrapperTypes.size() != typeWrappers.size() + vivifiedTypeWrappers.size()) {
+ for (DexType type : typeWrappers.keySet()) {
+ DexType typeWrapperType = typeWrappers.get(type);
+ if (!synthesizedWrapperTypes.contains(typeWrapperType)) {
+ synthesizedWrappers.add(generateTypeWrapper(getValidClassToWrap(type), typeWrapperType));
+ synthesizedWrapperTypes.add(typeWrapperType);
+ }
+ }
+ for (DexType type : vivifiedTypeWrappers.keySet()) {
+ DexType vivifiedTypeWrapperType = vivifiedTypeWrappers.get(type);
+ if (!synthesizedWrapperTypes.contains(vivifiedTypeWrapperType)) {
+ synthesizedWrappers.add(
+ generateVivifiedTypeWrapper(getValidClassToWrap(type), vivifiedTypeWrapperType));
+ synthesizedWrapperTypes.add(vivifiedTypeWrapperType);
+ }
+ }
}
+ assert synthesizedWrappers.size() == synthesizedWrapperTypes.size();
+ return synthesizedWrappers;
}
- private void finalizeWrappers(
+ private void registerAndProcessWrappers(
+ DexApplication.Builder<?> builder,
IRConverter irConverter,
ExecutorService executorService,
- Map<DexType, Pair<DexType, DexProgramClass>> wrappers,
- BiConsumer<DexType, DexProgramClass> generateConversions)
+ List<DexProgramClass> wrappers)
throws ExecutionException {
- for (DexType type : wrappers.keySet()) {
- DexProgramClass pgrmClass = wrappers.get(type).getSecond();
- assert pgrmClass != null;
- generateConversions.accept(type, pgrmClass);
- irConverter.optimizeSynthesizedClass(pgrmClass, executorService);
+ for (DexProgramClass wrapper : wrappers) {
+ builder.addSynthesizedClass(wrapper, false);
+ appView.appInfo().addSynthesizedClass(wrapper);
}
+ irConverter.optimizeSynthesizedClasses(wrappers, executorService);
}
- private boolean verifyAllClassesGenerated() {
- for (Pair<DexType, DexProgramClass> pair : vivifiedTypeWrappers.values()) {
- assert pair.getSecond() != null;
- }
- for (Pair<DexType, DexProgramClass> pair : typeWrappers.values()) {
- assert pair.getSecond() != null;
- }
- return true;
+ private DexEncodedMethod generateTypeConversion(DexType type, DexType typeWrapperType) {
+ DexType reverse = vivifiedTypeWrappers.get(type);
+ return synthesizeConversionMethod(
+ typeWrapperType,
+ type,
+ type,
+ vivifiedTypeFor(type),
+ reverse == null ? null : wrappedValueField(reverse, vivifiedTypeFor(type)));
}
- private void registerSynthesizedClass(
- DexProgramClass synthesizedClass, DexApplication.Builder<?> builder) {
- builder.addSynthesizedClass(synthesizedClass, false);
- appView.appInfo().addSynthesizedClass(synthesizedClass);
- }
-
- private void generateTypeConversions(DexType type, DexProgramClass synthesizedClass) {
- Pair<DexType, DexProgramClass> reverse = vivifiedTypeWrappers.get(type);
- assert reverse == null || reverse.getSecond() != null;
- synthesizedClass.addDirectMethod(
- synthesizeConversionMethod(
- synthesizedClass.type,
- type,
- type,
- vivifiedTypeFor(type),
- reverse == null ? null : reverse.getSecond()));
- }
-
- private void generateVivifiedTypeConversions(DexType type, DexProgramClass synthesizedClass) {
- Pair<DexType, DexProgramClass> reverse = typeWrappers.get(type);
- synthesizedClass.addDirectMethod(
- synthesizeConversionMethod(
- synthesizedClass.type,
- type,
- vivifiedTypeFor(type),
- type,
- reverse == null ? null : reverse.getSecond()));
+ private DexEncodedMethod generateVivifiedTypeConversion(
+ DexType type, DexType vivifiedTypeWrapperType) {
+ DexType reverse = typeWrappers.get(type);
+ return synthesizeConversionMethod(
+ vivifiedTypeWrapperType,
+ type,
+ vivifiedTypeFor(type),
+ type,
+ reverse == null ? null : wrappedValueField(reverse, type));
}
private DexEncodedMethod synthesizeConversionMethod(
@@ -566,7 +526,7 @@
DexType type,
DexType argType,
DexType returnType,
- DexClass reverseWrapperClassOrNull) {
+ DexField reverseFieldOrNull) {
DexMethod method =
factory.createMethod(
holder, factory.createProto(returnType, argType), factory.convertMethodName);
@@ -582,15 +542,11 @@
holder)
.generateCfCode();
} else {
- DexField uniqueFieldOrNull =
- reverseWrapperClassOrNull == null
- ? null
- : reverseWrapperClassOrNull.instanceFields().get(0).field;
cfCode =
new APIConverterWrapperConversionCfCodeProvider(
appView,
argType,
- uniqueFieldOrNull,
+ reverseFieldOrNull,
factory.createField(holder, returnType, factory.wrapperFieldName))
.generateCfCode();
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 7620ac7..5ff8e57 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -1042,27 +1042,28 @@
return false;
}
- public void warnMissingInterface(
- DexClass classToDesugar, DexClass implementing, DexType missing) {
+ private boolean shouldIgnoreFromReports(DexType missing) {
+ return appView.rewritePrefix.hasRewrittenType(missing)
+ || DesugaredLibraryWrapperSynthesizer.isSynthesizedWrapper(missing)
+ || DesugaredLibraryAPIConverter.isVivifiedType(missing)
+ || isCompanionClassType(missing)
+ || emulatedInterfaces.containsValue(missing)
+ || options.desugaredLibraryConfiguration.getCustomConversions().containsValue(missing);
+ }
+
+ void warnMissingInterface(DexClass classToDesugar, DexClass implementing, DexType missing) {
// We use contains() on non hashed collection, but we know it's a 8 cases collection.
// j$ interfaces won't be missing, they are in the desugared library.
- if (!emulatedInterfaces.values().contains(missing)) {
- options.warningMissingInterfaceForDesugar(classToDesugar, implementing, missing);
+ if (shouldIgnoreFromReports(missing)) {
+ return;
}
+ options.warningMissingInterfaceForDesugar(classToDesugar, implementing, missing);
}
private void warnMissingType(DexMethod referencedFrom, DexType missing) {
// Companion/Emulated interface/Conversion classes for desugared library won't be missing,
// they are in the desugared library.
- if (appView.rewritePrefix.hasRewrittenType(missing)
- || DesugaredLibraryWrapperSynthesizer.isSynthesizedWrapper(missing)
- || isCompanionClassType(missing)
- || appView
- .options()
- .desugaredLibraryConfiguration
- .getCustomConversions()
- .values()
- .contains(missing)) {
+ if (shouldIgnoreFromReports(missing)) {
return;
}
DexMethod method = appView.graphLense().getOriginalMethodSignature(referencedFrom);