Move synthesis of conversion wrappers to the fixed point.
Bug: 151074209
Change-Id: I2673a24beadea23a477268e7165ded4bfd24f653
diff --git a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
index b8210c8..03c329c 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -243,7 +243,7 @@
return self();
}
- public Builder addClasspathClasses(List<DexClasspathClass> classes) {
+ public Builder addClasspathClasses(Collection<DexClasspathClass> classes) {
classpathClasses =
ImmutableList.<DexClasspathClass>builder()
.addAll(classpathClasses)
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java
index d8d48a2..8d61294 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java
@@ -15,18 +15,16 @@
import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter.Mode;
import com.android.tools.r8.utils.OptionalBool;
import java.util.ArrayList;
+import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
-import java.util.function.Consumer;
public class DesugaredLibraryConversionWrapperAnalysis extends EnqueuerAnalysis
implements EnqueuerInvokeAnalysis {
private final AppView<?> appView;
private final DesugaredLibraryAPIConverter converter;
- private boolean callbackGenerated = false;
- private Map<DexProgramClass, DexProgramClass> wrappersToReverseMap = null;
+ private Map<DexType, DexProgramClass> synthesizedWrappers = new IdentityHashMap<>();
public DesugaredLibraryConversionWrapperAnalysis(AppView<?> appView) {
this.appView = appView;
@@ -69,22 +67,18 @@
}
public List<DexEncodedMethod> generateCallbackMethods() {
- assert !callbackGenerated;
- callbackGenerated = true;
return converter.generateCallbackMethods();
}
- public Set<DexProgramClass> generateWrappers() {
- assert wrappersToReverseMap == null;
- wrappersToReverseMap = converter.synthesizeWrappersAndMapToReverse();
- return wrappersToReverseMap.keySet();
+ public List<DexProgramClass> generateWrappers() {
+ return converter.synthesizeWrappers(synthesizedWrappers);
}
// Generate a mock classpath class for all vivified types.
// Types will be available at runtime in the desugared library dex file.
- public List<DexClasspathClass> generateWrappersSuperTypeMock() {
+ public List<DexClasspathClass> generateWrappersSuperTypeMock(List<DexProgramClass> wrappers) {
List<DexClasspathClass> classpathClasses = new ArrayList<>();
- for (DexProgramClass wrapper : wrappersToReverseMap.keySet()) {
+ for (DexProgramClass wrapper : wrappers) {
boolean mockIsInterface = wrapper.interfaces.size() == 1;
DexType mockType = mockIsInterface ? wrapper.interfaces.values[0] : wrapper.superType;
if (appView.definitionFor(mockType) == null) {
@@ -106,37 +100,4 @@
}
return classpathClasses;
}
-
- public DesugaredLibraryConversionWrapperAnalysis registerWrite(
- DexProgramClass wrapper, Consumer<DexEncodedMethod> registration) {
- registration.accept(getInitializer(wrapper));
- return this;
- }
-
- public DesugaredLibraryConversionWrapperAnalysis registerReads(
- DexProgramClass wrapper, Consumer<DexEncodedMethod> registration) {
- // The field of each wrapper is read exclusively in all virtual methods and the reverse wrapper
- // convert method.
- for (DexEncodedMethod virtualMethod : wrapper.virtualMethods()) {
- registration.accept(virtualMethod);
- }
- DexProgramClass reverseWrapper = wrappersToReverseMap.get(wrapper);
- assert reverseWrapper != null;
- registration.accept(getConvertMethod(reverseWrapper));
- return this;
- }
-
- private DexEncodedMethod getInitializer(DexProgramClass wrapper) {
- DexEncodedMethod initializer =
- wrapper.lookupDirectMethod(DexEncodedMethod::isInstanceInitializer);
- assert initializer != null;
- return initializer;
- }
-
- private DexEncodedMethod getConvertMethod(DexProgramClass wrapper) {
- DexEncodedMethod convertMethod = wrapper.lookupDirectMethod(DexEncodedMethod::isStatic);
- assert convertMethod != null;
- assert convertMethod.method.name == appView.dexItemFactory().convertMethodName;
- return convertMethod;
- }
}
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 fb97d0c..45c5bd2 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
@@ -29,14 +29,15 @@
import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterWrapperCfCodeProvider;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.WorkList;
import com.google.common.collect.Sets;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
-import java.util.LinkedList;
+import java.util.IdentityHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
@@ -70,7 +71,9 @@
// Enqueuer and not during IR processing.
private final Mode mode;
private final DesugaredLibraryWrapperSynthesizer wrapperSynthesizor;
- private final Map<DexClass, Set<DexEncodedMethod>> callBackMethods = new HashMap<>();
+ private final Map<DexClass, Set<DexEncodedMethod>> callBackMethods = new IdentityHashMap<>();
+ private final Map<DexClass, List<DexEncodedMethod>> pendingCallBackMethods =
+ new IdentityHashMap<>();
private final Set<DexMethod> trackedCallBackAPIs;
private final Set<DexMethod> trackedAPIs;
@@ -172,7 +175,9 @@
// library type), but the enqueuer cannot see that.
// To avoid too much computation we first look if the method would need to be rewritten if
// it would override a library method, then check if it overrides a library method.
- if (encodedMethod.isPrivateMethod() || encodedMethod.isStatic()) {
+ if (encodedMethod.isPrivateMethod()
+ || encodedMethod.isStatic()
+ || encodedMethod.isLibraryMethodOverride().isFalse()) {
return false;
}
DexMethod method = encodedMethod.method;
@@ -195,22 +200,22 @@
private boolean overridesLibraryMethod(DexClass theClass, DexMethod method) {
// We look up everywhere to see if there is a supertype/interface implementing the method...
- LinkedList<DexType> workList = new LinkedList<>();
- Collections.addAll(workList, theClass.interfaces.values);
+ WorkList<DexType> workList = WorkList.newIdentityWorkList();
+ workList.addIfNotSeen(theClass.interfaces.values);
boolean foundOverrideToRewrite = false;
// There is no methods with desugared types on Object.
if (theClass.superType != factory.objectType) {
- workList.add(theClass.superType);
+ workList.addIfNotSeen(theClass.superType);
}
- while (!workList.isEmpty()) {
- DexType current = workList.removeFirst();
+ while (workList.hasNext()) {
+ DexType current = workList.next();
DexClass dexClass = appView.definitionFor(current);
if (dexClass == null) {
continue;
}
- workList.addAll(Arrays.asList(dexClass.interfaces.values));
+ workList.addIfNotSeen(dexClass.interfaces.values);
if (dexClass.superType != factory.objectType) {
- workList.add(dexClass.superType);
+ workList.addIfNotSeen(dexClass.superType);
}
if (!dexClass.isLibraryClass() && !appView.options().isDesugaredLibraryCompilation()) {
continue;
@@ -249,8 +254,9 @@
private synchronized void addCallBackSignature(DexClass dexClass, DexEncodedMethod method) {
assert dexClass.type == method.method.holder;
- callBackMethods.putIfAbsent(dexClass, new HashSet<>());
- callBackMethods.get(dexClass).add(method);
+ if (callBackMethods.computeIfAbsent(dexClass, key -> new HashSet<>()).add(method)) {
+ pendingCallBackMethods.computeIfAbsent(dexClass, key -> new ArrayList<>()).add(method);
+ }
}
public static DexMethod methodWithVivifiedTypeInSignature(
@@ -289,19 +295,24 @@
if (appView.options().testing.trackDesugaredAPIConversions) {
generateTrackDesugaredAPIWarnings(trackedAPIs, "");
generateTrackDesugaredAPIWarnings(trackedCallBackAPIs, "callback ");
+ trackedAPIs.clear();
+ trackedCallBackAPIs.clear();
}
List<DexEncodedMethod> result = new ArrayList<>();
- for (DexClass dexClass : callBackMethods.keySet()) {
- List<DexEncodedMethod> dexEncodedMethods =
- generateCallbackMethods(callBackMethods.get(dexClass), dexClass);
- dexClass.addVirtualMethods(dexEncodedMethods);
- result.addAll(dexEncodedMethods);
- }
+ pendingCallBackMethods.forEach(
+ (clazz, callbacks) -> {
+ List<DexEncodedMethod> generated =
+ ListUtils.map(callbacks, callback -> generateCallbackMethod(callback, clazz));
+ clazz.addVirtualMethods(generated);
+ result.addAll(generated);
+ });
+ pendingCallBackMethods.clear();
return result;
}
- public Map<DexProgramClass, DexProgramClass> synthesizeWrappersAndMapToReverse() {
- return wrapperSynthesizor.synthesizeWrappersAndMapToReverse();
+ public List<DexProgramClass> synthesizeWrappers(
+ Map<DexType, DexProgramClass> synthesizedWrappers) {
+ return wrapperSynthesizor.synthesizeWrappers(synthesizedWrappers);
}
public DexClasspathClass synthesizeClasspathMock(
@@ -309,23 +320,21 @@
return wrapperSynthesizor.synthesizeClasspathMock(classToMock, mockType, mockIsInterface);
}
- private List<DexEncodedMethod> generateCallbackMethods(
- Set<DexEncodedMethod> originalMethods, DexClass dexClass) {
- List<DexEncodedMethod> newDexEncodedMethods = new ArrayList<>();
- for (DexEncodedMethod originalMethod : originalMethods) {
- DexMethod methodToInstall =
- methodWithVivifiedTypeInSignature(originalMethod.method, dexClass.type, appView);
- CfCode cfCode =
- new APIConverterWrapperCfCodeProvider(
- appView, originalMethod.method, null, this, dexClass.isInterface())
- .generateCfCode();
- DexEncodedMethod newDexEncodedMethod =
- wrapperSynthesizor.newSynthesizedMethod(methodToInstall, originalMethod, cfCode);
- newDexEncodedMethod.setCode(cfCode, appView);
- newDexEncodedMethods.add(newDexEncodedMethod);
+ private DexEncodedMethod generateCallbackMethod(
+ DexEncodedMethod originalMethod, DexClass dexClass) {
+ DexMethod methodToInstall =
+ methodWithVivifiedTypeInSignature(originalMethod.method, dexClass.type, appView);
+ CfCode cfCode =
+ new APIConverterWrapperCfCodeProvider(
+ appView, originalMethod.method, null, this, dexClass.isInterface())
+ .generateCfCode();
+ DexEncodedMethod newDexEncodedMethod =
+ wrapperSynthesizor.newSynthesizedMethod(methodToInstall, originalMethod, cfCode);
+ newDexEncodedMethod.setCode(cfCode, appView);
+ if (originalMethod.isLibraryMethodOverride().isTrue()) {
+ newDexEncodedMethod.setLibraryMethodOverride(OptionalBool.TRUE);
}
- assert Sets.newHashSet(newDexEncodedMethods).size() == newDexEncodedMethods.size();
- return newDexEncodedMethods;
+ return newDexEncodedMethod;
}
private void generateTrackDesugaredAPIWarnings(Set<DexMethod> tracked, String inner) {
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 946077d..30bc78e 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
@@ -506,52 +506,33 @@
void finalizeWrappersForD8(
DexApplication.Builder<?> builder, IRConverter irConverter, ExecutorService executorService)
throws ExecutionException {
- Map<DexType, DexProgramClass> synthesizedWrappers = synthesizeWrappers();
- registerAndProcessWrappers(builder, irConverter, executorService, synthesizedWrappers.values());
+ List<DexProgramClass> synthesizedWrappers = synthesizeWrappers(new IdentityHashMap<>());
+ registerAndProcessWrappers(builder, irConverter, executorService, synthesizedWrappers);
}
- private Map<DexType, DexProgramClass> synthesizeWrappers() {
- Map<DexType, DexProgramClass> synthesizedWrappers = new IdentityHashMap<>();
+ List<DexProgramClass> synthesizeWrappers(Map<DexType, DexProgramClass> synthesizedWrappers) {
+ List<DexProgramClass> additions = new ArrayList<>();
// Generating a wrapper may require other wrappers to be generated, iterate until fix point.
while (synthesizedWrappers.size() != typeWrappers.size() + vivifiedTypeWrappers.size()) {
for (DexType type : typeWrappers.keySet()) {
DexType typeWrapperType = typeWrappers.get(type);
if (!synthesizedWrappers.containsKey(typeWrapperType)) {
- synthesizedWrappers.put(
- typeWrapperType, generateTypeWrapper(getValidClassToWrap(type), typeWrapperType));
+ DexProgramClass wrapper = generateTypeWrapper(getValidClassToWrap(type), typeWrapperType);
+ synthesizedWrappers.put(typeWrapperType, wrapper);
+ additions.add(wrapper);
}
}
for (DexType type : vivifiedTypeWrappers.keySet()) {
DexType vivifiedTypeWrapperType = vivifiedTypeWrappers.get(type);
if (!synthesizedWrappers.containsKey(vivifiedTypeWrapperType)) {
- synthesizedWrappers.put(
- vivifiedTypeWrapperType,
- generateVivifiedTypeWrapper(getValidClassToWrap(type), vivifiedTypeWrapperType));
+ DexProgramClass wrapper =
+ generateVivifiedTypeWrapper(getValidClassToWrap(type), vivifiedTypeWrapperType);
+ synthesizedWrappers.put(vivifiedTypeWrapperType, wrapper);
+ additions.add(wrapper);
}
}
}
- return synthesizedWrappers;
- }
-
- private Map<DexType, DexType> reverseWrapperMap() {
- Map<DexType, DexType> reverseWrapperMap = new IdentityHashMap<>();
- for (DexType type : typeWrappers.keySet()) {
- reverseWrapperMap.put(typeWrappers.get(type), vivifiedTypeWrappers.get(type));
- }
- for (DexType type : vivifiedTypeWrappers.keySet()) {
- reverseWrapperMap.put(vivifiedTypeWrappers.get(type), typeWrappers.get(type));
- }
- return reverseWrapperMap;
- }
-
- Map<DexProgramClass, DexProgramClass> synthesizeWrappersAndMapToReverse() {
- Map<DexType, DexProgramClass> synthesizedWrappers = synthesizeWrappers();
- Map<DexType, DexType> reverseMap = reverseWrapperMap();
- Map<DexProgramClass, DexProgramClass> wrappersAndReverse = new IdentityHashMap<>();
- for (DexProgramClass wrapper : synthesizedWrappers.values()) {
- wrappersAndReverse.put(wrapper, synthesizedWrappers.get(reverseMap.get(wrapper.type)));
- }
- return wrappersAndReverse;
+ return additions;
}
private void registerAndProcessWrappers(
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 6c7a4fe..20dde1b 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -2514,6 +2514,8 @@
Map<DexMethod, ProgramMethod> liveMethods = new IdentityHashMap<>();
+ Map<DexType, DexClasspathClass> syntheticClasspathClasses = new IdentityHashMap<>();
+
// Subset of live methods that need to be pinned.
Set<DexMethod> pinnedMethods = Sets.newIdentityHashSet();
@@ -2535,6 +2537,11 @@
}
}
+ void addClasspathClass(DexClasspathClass clazz) {
+ DexClasspathClass old = syntheticClasspathClasses.put(clazz.type, clazz);
+ assert old == null;
+ }
+
void addLiveMethod(ProgramMethod method) {
DexMethod signature = method.getMethod().method;
assert !liveMethods.containsKey(signature);
@@ -2552,6 +2559,7 @@
syntheticInstantiations.values()) {
appBuilder.addProgramClass(clazzAndContext.getFirst());
}
+ appBuilder.addClasspathClasses(syntheticClasspathClasses.values());
appBuilder.addToMainDexList(mainDexTypes);
}
@@ -2590,6 +2598,7 @@
SyntheticAdditions additions = new SyntheticAdditions();
synthesizeInterfaceMethodBridges(additions);
synthesizeLambdas(additions);
+ synthesizeLibraryConversionWrappers(additions);
if (additions.isEmpty()) {
return;
}
@@ -2714,7 +2723,6 @@
appBuilder.replaceClasspathClasses(classpathClasses);
// Can't replace the program classes at this point as they are needed in tree pruning.
// Post process the app to add synthetic content.
- postProcessLibraryConversionWrappers(appBuilder);
DirectMappedDexApplication app = appBuilder.build();
AppInfoWithLiveness appInfoWithLiveness =
@@ -2810,7 +2818,7 @@
}
}
- private void postProcessLibraryConversionWrappers(DirectMappedDexApplication.Builder appBuilder) {
+ private void synthesizeLibraryConversionWrappers(SyntheticAdditions additions) {
if (desugaredLibraryWrapperAnalysis == null) {
return;
}
@@ -2819,41 +2827,24 @@
List<DexEncodedMethod> callbacks = desugaredLibraryWrapperAnalysis.generateCallbackMethods();
for (DexEncodedMethod callback : callbacks) {
DexProgramClass clazz = getProgramClassOrNull(callback.method.holder);
- targetedMethods.add(callback, graphReporter.fakeReportShouldNotBeUsed());
- liveMethods.add(clazz, callback, graphReporter.fakeReportShouldNotBeUsed());
+ additions.addLiveMethod(new ProgramMethod(clazz, callback));
}
// Generate the wrappers.
- Set<DexProgramClass> wrappers = desugaredLibraryWrapperAnalysis.generateWrappers();
+ List<DexProgramClass> wrappers = desugaredLibraryWrapperAnalysis.generateWrappers();
for (DexProgramClass wrapper : wrappers) {
- appBuilder.addProgramClass(wrapper);
- liveTypes.add(wrapper, graphReporter.fakeReportShouldNotBeUsed());
- objectAllocationInfoCollection.recordDirectAllocationSite(
- wrapper,
- null,
- InstantiationReason.SYNTHESIZED_CLASS,
- graphReporter.fakeReportShouldNotBeUsed(),
- appInfo);
+ additions.addInstantiatedClass(wrapper, null, false);
// Mark all methods on the wrapper as live and targeted.
for (DexEncodedMethod method : wrapper.methods()) {
- targetedMethods.add(method, graphReporter.fakeReportShouldNotBeUsed());
- liveMethods.add(wrapper, method, graphReporter.fakeReportShouldNotBeUsed());
+ additions.addLiveMethod(new ProgramMethod(wrapper, method));
}
- // Register wrapper unique field reads and unique write.
- assert wrapper.instanceFields().size() == 1;
- DexField field = wrapper.instanceFields().get(0).field;
- FieldAccessInfoImpl info = new FieldAccessInfoImpl(field);
- fieldAccessInfoCollection.extend(field, info);
- desugaredLibraryWrapperAnalysis
- .registerWrite(wrapper, writeContext -> info.recordWrite(field, writeContext))
- .registerReads(wrapper, readContext -> info.recordRead(field, readContext));
}
// Add all vivified types as classpath classes.
// They will be available at runtime in the desugared library dex file.
- List<DexClasspathClass> mockVivifiedClasses =
- desugaredLibraryWrapperAnalysis.generateWrappersSuperTypeMock();
- appBuilder.addClasspathClasses(mockVivifiedClasses);
+ desugaredLibraryWrapperAnalysis
+ .generateWrappersSuperTypeMock(wrappers)
+ .forEach(additions::addClasspathClass);
}
private void rewriteLambdaCallSites(