Harmonize interface desugaring class processing
Bug: 183998768
Change-Id: Ib632a635cfc20d1c4675a3aa7ee2691655f60666
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index 7bf8f2f..9d9a502 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMember;
import com.android.tools.r8.graph.DexClassAndMethod;
@@ -22,7 +23,6 @@
import com.android.tools.r8.graph.LibraryMethod;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
-import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.ir.synthetic.ExceptionThrowingSourceCode;
import com.android.tools.r8.ir.synthetic.SynthesizedCode;
@@ -55,7 +55,7 @@
* In other words, the traversal is in top-down (edges from type to its subtypes) topological order.
* The traversal is lazy, starting from the unordered set of program classes.
*/
-final class ClassProcessor {
+final class ClassProcessor implements InterfaceDesugaringProcessor {
// Collection for method signatures that may cause forwarding methods to be created.
private static class MethodSignatures {
@@ -336,7 +336,6 @@
private final AppView<?> appView;
private final DexItemFactory dexItemFactory;
private final InterfaceMethodRewriter rewriter;
- private final Consumer<ProgramMethod> newSynthesizedMethodConsumer;
private final MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
private final boolean needsLibraryInfo;
@@ -353,14 +352,10 @@
private final Map<DexProgramClass, ProgramMethodSet> newSyntheticMethods =
new IdentityHashMap<>();
- ClassProcessor(
- AppView<?> appView,
- InterfaceMethodRewriter rewriter,
- Consumer<ProgramMethod> newSynthesizedMethodConsumer) {
+ ClassProcessor(AppView<?> appView, InterfaceMethodRewriter rewriter) {
this.appView = appView;
this.dexItemFactory = appView.dexItemFactory();
this.rewriter = rewriter;
- this.newSynthesizedMethodConsumer = newSynthesizedMethodConsumer;
needsLibraryInfo =
!appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface().isEmpty()
|| !appView
@@ -378,15 +373,25 @@
return !needsLibraryInfo;
}
- public void processClass(DexProgramClass clazz) {
+ @Override
+ public void process(DexProgramClass clazz, ProgramMethodSet synthesizedMethods) {
visitClassInfo(clazz, new ReportingContext(clazz, clazz));
}
- final void addSyntheticMethods() {
+ @Override
+ public boolean shouldProcess(DexProgramClass clazz) {
+ return !clazz.isInterface();
+ }
+
+ // We introduce forwarding methods only once all desugaring has been performed to avoid
+ // confusing the look-up with inserted forwarding methods.
+ @Override
+ public final void finalizeProcessing(
+ DexApplication.Builder<?> builder, ProgramMethodSet synthesizedMethods) {
newSyntheticMethods.forEach(
(clazz, newForwardingMethods) -> {
clazz.addVirtualMethods(newForwardingMethods.toDefinitionSet());
- newForwardingMethods.forEach(newSynthesizedMethodConsumer);
+ newForwardingMethods.forEach(synthesizedMethods::add);
});
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
index b786f7b..2f06a5e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
@@ -33,7 +33,7 @@
import java.util.Map;
import java.util.Set;
-public final class EmulatedInterfaceProcessor {
+public final class EmulatedInterfaceProcessor implements InterfaceDesugaringProcessor {
private final AppView<?> appView;
private final InterfaceMethodRewriter rewriter;
private final Map<DexType, DexType> emulatedInterfaces;
@@ -268,6 +268,23 @@
return false;
}
+ @Override
+ public boolean shouldProcess(DexProgramClass clazz) {
+ return appView.options().isDesugaredLibraryCompilation()
+ && rewriter.isEmulatedInterface(clazz.type);
+ }
+
+ @Override
+ public void process(DexProgramClass clazz, ProgramMethodSet synthesizedMethods) {
+ // TODO(b/183998768): Due to sequential dependencies we cannot generateEmulateInterfaceLibrary
+ // and replaceInterfacesInEmulatedInterface here. We need to merge this into a single loop.
+ // Uncomment these two lines instead of running them separately.
+ // generateEmulateInterfaceLibrary(clazz);
+ // replaceInterfacesInEmulatedInterface(clazz);
+ renameEmulatedInterface(clazz);
+ }
+
+ @Override
public void finalizeProcessing(
DexApplication.Builder<?> builder, ProgramMethodSet synthesizedMethods) {
warnMissingEmulatedInterfaces();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringProcessor.java
new file mode 100644
index 0000000..d85e8a2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringProcessor.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.itf;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
+
+public interface InterfaceDesugaringProcessor {
+ boolean shouldProcess(DexProgramClass clazz);
+
+ void process(DexProgramClass clazz, ProgramMethodSet synthesizedMethods);
+
+ void finalizeProcessing(DexApplication.Builder<?> builder, ProgramMethodSet synthesizedMethods);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index c0e00a3..3ce00a2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -62,7 +62,6 @@
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
import com.android.tools.r8.ir.desugar.itf.DefaultMethodsHelper.Collection;
-import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor.InterfaceProcessorNestedGraphLens;
import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations;
import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.MethodSynthesizerConsumer;
import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.UtilityMethodForCodeOptimizations;
@@ -205,7 +204,7 @@
}
}
- public void addRewritePrefix(DexType interfaceType, String rewrittenType) {
+ void addRewritePrefix(DexType interfaceType, String rewrittenType) {
appView.rewritePrefix.rewriteType(
getCompanionClassType(interfaceType),
factory.createType(
@@ -217,7 +216,7 @@
rewrittenType + EMULATE_LIBRARY_CLASS_NAME_SUFFIX)));
}
- public boolean isEmulatedInterface(DexType itf) {
+ boolean isEmulatedInterface(DexType itf) {
return emulatedInterfaces.containsKey(itf);
}
@@ -769,7 +768,7 @@
return false;
}
- public DexMethod emulateInterfaceLibraryMethod(DexClassAndMethod method) {
+ DexMethod emulateInterfaceLibraryMethod(DexClassAndMethod method) {
return factory.createMethod(
getEmulateLibraryInterfaceClassType(method.getHolderType(), factory),
factory.prependTypeToProto(method.getHolderType(), method.getProto()),
@@ -782,7 +781,7 @@
+ ";";
}
- public static DexType getEmulateLibraryInterfaceClassType(DexType type, DexItemFactory factory) {
+ static DexType getEmulateLibraryInterfaceClassType(DexType type, DexItemFactory factory) {
assert type.isClassType();
String descriptor = type.descriptor.toString();
String elTypeDescriptor = getEmulateLibraryInterfaceClassDescriptor(descriptor);
@@ -846,7 +845,7 @@
}
// Represent a static interface method as a method of companion class.
- public final DexMethod staticAsMethodOfCompanionClass(DexClassAndMethod method) {
+ final DexMethod staticAsMethodOfCompanionClass(DexClassAndMethod method) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
DexType companionClassType = getCompanionClassType(method.getHolderType(), dexItemFactory);
DexMethod rewritten = method.getReference().withHolder(companionClassType, dexItemFactory);
@@ -958,14 +957,18 @@
public void desugarInterfaceMethods(
Builder<?> builder, Flavor flavour, ExecutorService executorService)
throws ExecutionException {
+ // TODO(b/183998768): Merge the following five sequential loops into a single one.
+
EmulatedInterfaceProcessor emulatedInterfaceProcessor =
new EmulatedInterfaceProcessor(appView, this);
+ // TODO(b/183998768): Move this to emulatedInterfaceProcessor#process
forEachProgramEmulatedInterface(emulatedInterfaceProcessor::generateEmulateInterfaceLibrary);
// Process all classes first. Add missing forwarding methods to
// replace desugared default interface methods.
- processClasses(builder, flavour, synthesizedMethods::add);
+ process(new ClassProcessor(appView, this), builder, flavour);
+ // TODO(b/183998768): Move this to emulatedInterfaceProcessor#process
forEachProgramEmulatedInterface(
emulatedInterfaceProcessor::replaceInterfacesInEmulatedInterface);
@@ -973,26 +976,11 @@
// methods to companion class, copy default interface methods to companion classes,
// make original default methods abstract, remove bridge methods, create dispatch
// classes if needed.
- AppInfo appInfo = appView.appInfo();
- InterfaceProcessorNestedGraphLens.Builder graphLensBuilder =
- InterfaceProcessorNestedGraphLens.builder();
- Map<DexClass, DexProgramClass> classMapping =
- processInterfaces(builder, flavour, graphLensBuilder, synthesizedMethods::add);
- InterfaceProcessorNestedGraphLens graphLens = graphLensBuilder.build(appView);
- if (appView.enableWholeProgramOptimizations() && graphLens != null) {
- appView.setGraphLens(graphLens);
- }
- classMapping.forEach(
- (interfaceClass, synthesizedClass) -> {
- // Don't need to optimize synthesized class since all of its methods
- // are just moved from interfaces and don't need to be re-processed.
- builder.addSynthesizedClass(synthesizedClass);
- appInfo.addSynthesizedClass(synthesizedClass, interfaceClass.asProgramClass());
- });
- new InterfaceMethodRewriterFixup(appView, graphLens).run();
+ process(new InterfaceProcessor(appView, this), builder, flavour);
- forEachProgramEmulatedInterface(emulatedInterfaceProcessor::renameEmulatedInterface);
- emulatedInterfaceProcessor.finalizeProcessing(builder, synthesizedMethods);
+ // During L8 compilation, emulated interfaces are processed to be renamed, to have
+ // their interfaces fixed-up and to generate the emulated dispatch code.
+ process(emulatedInterfaceProcessor, builder, flavour);
converter.processMethodsConcurrently(synthesizedMethods, executorService);
@@ -1017,41 +1005,20 @@
this.synthesizedMethods.clear();
}
- private static boolean shouldProcess(
- DexProgramClass clazz, Flavor flavour, boolean mustBeInterface) {
- return (!clazz.originatesFromDexResource() || flavour == Flavor.IncludeAllResources)
- && clazz.isInterface() == mustBeInterface;
+ private boolean shouldProcess(DexProgramClass clazz, Flavor flavour) {
+ if (appView.isAlreadyLibraryDesugared(clazz)) {
+ return false;
+ }
+ return (!clazz.originatesFromDexResource() || flavour == Flavor.IncludeAllResources);
}
- private Map<DexClass, DexProgramClass> processInterfaces(
- Builder<?> builder,
- Flavor flavour,
- InterfaceProcessorNestedGraphLens.Builder graphLensBuilder,
- Consumer<ProgramMethod> newSynthesizedMethodConsumer) {
- InterfaceProcessor processor = new InterfaceProcessor(appView, this);
+ private void process(InterfaceDesugaringProcessor processor, Builder<?> builder, Flavor flavour) {
for (DexProgramClass clazz : builder.getProgramClasses()) {
- if (shouldProcess(clazz, flavour, true)) {
- processor.process(clazz, graphLensBuilder, newSynthesizedMethodConsumer);
+ if (shouldProcess(clazz, flavour) && processor.shouldProcess(clazz)) {
+ processor.process(clazz, synthesizedMethods);
}
}
- return processor.syntheticClasses;
- }
-
- private void processClasses(
- Builder<?> builder, Flavor flavour, Consumer<ProgramMethod> newSynthesizedMethodConsumer) {
- ClassProcessor processor = new ClassProcessor(appView, this, newSynthesizedMethodConsumer);
- // First we compute all desugaring *without* introducing forwarding methods.
- assert appView.getSyntheticItems().verifyNonLegacySyntheticsAreCommitted();
- for (DexProgramClass clazz : builder.getProgramClasses()) {
- if (shouldProcess(clazz, flavour, false)) {
- if (appView.isAlreadyLibraryDesugared(clazz)) {
- continue;
- }
- processor.processClass(clazz);
- }
- }
- // Then we introduce forwarding methods.
- processor.addSyntheticMethods();
+ processor.finalizeProcessing(builder, synthesizedMethods);
}
final boolean isDefaultMethod(DexEncodedMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index 4caff1a..32c8c64 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexApplication.Builder;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -50,6 +51,7 @@
import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.ImmutableList;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -60,7 +62,6 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.function.Consumer;
import org.objectweb.asm.Opcodes;
// Default and static method interface desugaring processor for interfaces.
@@ -69,23 +70,28 @@
// a companion class. Removes bridge default methods.
//
// Also moves static interface methods into a companion class.
-public final class InterfaceProcessor {
+public final class InterfaceProcessor implements InterfaceDesugaringProcessor {
private final AppView<?> appView;
private final InterfaceMethodRewriter rewriter;
+ private final InterfaceProcessorNestedGraphLens.Builder graphLensBuilder;
- // All created companion and dispatch classes indexed by interface type.
+ // All created companion classes indexed by interface type.
final Map<DexClass, DexProgramClass> syntheticClasses = new IdentityHashMap<>();
InterfaceProcessor(AppView<?> appView, InterfaceMethodRewriter rewriter) {
this.appView = appView;
this.rewriter = rewriter;
+ graphLensBuilder = InterfaceProcessorNestedGraphLens.builder();
}
- void process(
- DexProgramClass iface,
- InterfaceProcessorNestedGraphLens.Builder graphLensBuilder,
- Consumer<ProgramMethod> newSynthesizedMethodConsumer) {
+ @Override
+ public boolean shouldProcess(DexProgramClass clazz) {
+ return clazz.isInterface();
+ }
+
+ @Override
+ public void process(DexProgramClass iface, ProgramMethodSet synthesizedMethods) {
assert iface.isInterface();
// The list of methods to be created in companion class.
List<DexEncodedMethod> companionMethods = new ArrayList<>();
@@ -137,7 +143,7 @@
getChecksumSupplier(iface));
syntheticClasses.put(iface, companionClass);
if (companionClass.hasClassInitializer()) {
- newSynthesizedMethodConsumer.accept(companionClass.getProgramClassInitializer());
+ synthesizedMethods.add(companionClass.getProgramClassInitializer());
}
}
@@ -438,6 +444,22 @@
&& !rewriter.factory.isClassConstructor(method.getReference());
}
+ @Override
+ public void finalizeProcessing(Builder<?> builder, ProgramMethodSet synthesizedMethods) {
+ InterfaceProcessorNestedGraphLens graphLens = graphLensBuilder.build(appView);
+ if (appView.enableWholeProgramOptimizations() && graphLens != null) {
+ appView.setGraphLens(graphLens);
+ }
+ syntheticClasses.forEach(
+ (interfaceClass, synthesizedClass) -> {
+ // Don't need to optimize synthesized class since all of its methods
+ // are just moved from interfaces and don't need to be re-processed.
+ builder.addSynthesizedClass(synthesizedClass);
+ appView.appInfo().addSynthesizedClass(synthesizedClass, interfaceClass.asProgramClass());
+ });
+ new InterfaceMethodRewriterFixup(appView, graphLens).run();
+ }
+
// Specific lens which remaps invocation types to static since all rewrites performed here
// are to static companion methods.
public static class InterfaceProcessorNestedGraphLens extends NestedGraphLens {