Emulated library class in new synthetic infrastructure
Bug: 183998768
Change-Id: I5bc2252b506aa1a22a5d19fe873e07433a10c14e
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index d387d77..3fd5ce0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1245,30 +1245,6 @@
return builder.build();
}
- public static DexEncodedMethod toEmulateDispatchLibraryMethod(
- DexType interfaceType,
- DexMethod newMethod,
- DexMethod companionMethod,
- DexMethod libraryMethod,
- List<Pair<DexType, DexMethod>> extraDispatchCases,
- AppView<?> appView) {
- MethodAccessFlags accessFlags =
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_SYNTHETIC | Constants.ACC_STATIC | Constants.ACC_PUBLIC, false);
- CfCode code =
- new EmulateInterfaceSyntheticCfCodeProvider(
- interfaceType, companionMethod, libraryMethod, extraDispatchCases, appView)
- .generateCfCode();
- return new DexEncodedMethod(
- newMethod,
- accessFlags,
- MethodTypeSignature.noSignature(),
- DexAnnotationSet.empty(),
- ParameterAnnotationsList.empty(),
- code,
- true);
- }
-
public ProgramMethod toStaticForwardingBridge(
DexProgramClass holder, DexMethod newMethod, DexItemFactory dexItemFactory) {
assert isPrivate()
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
index 4f4673a..a6a5984 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexApplication;
@@ -42,6 +43,7 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.synthetic.EmulateInterfaceSyntheticCfCodeProvider;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -677,13 +679,25 @@
appView
.dexItemFactory()
.createMethod(dispatchHolder, desugarMethod.proto, emulatedDispatchMethod.getName());
- return DexEncodedMethod.toEmulateDispatchLibraryMethod(
- emulatedDispatchMethod.getHolderType(),
+ MethodAccessFlags accessFlags =
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_SYNTHETIC | Constants.ACC_STATIC | Constants.ACC_PUBLIC, false);
+ CfCode code =
+ new EmulateInterfaceSyntheticCfCodeProvider(
+ emulatedDispatchMethod.getHolderType(),
+ desugarMethod,
+ itfMethod,
+ Collections.emptyList(),
+ appView)
+ .generateCfCode();
+ return new DexEncodedMethod(
newMethod,
- desugarMethod,
- itfMethod,
- Collections.emptyList(),
- appView);
+ accessFlags,
+ MethodTypeSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ ParameterAnnotationsList.empty(),
+ code,
+ true);
}
}
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 9efdc49..eac8a2b 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
@@ -3,21 +3,22 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.desugar.itf;
+import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.ClassAccessFlags;
-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;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
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.GenericSignature;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
-import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.synthetic.EmulateInterfaceSyntheticCfCodeProvider;
+import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
+import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.Pair;
@@ -39,8 +40,6 @@
private final InterfaceMethodRewriter rewriter;
private final Map<DexType, DexType> emulatedInterfaces;
private final Map<DexType, List<DexType>> emulatedInterfacesHierarchy;
- // All created emulated interface classes indexed by emulated interface type.
- final Map<DexProgramClass, DexProgramClass> syntheticClasses = new IdentityHashMap<>();
EmulatedInterfaceProcessor(AppView<?> appView, InterfaceMethodRewriter rewriter) {
this.appView = appView;
@@ -135,125 +134,118 @@
return newMethods;
}
- void generateEmulateInterfaceLibrary(DexProgramClass emulatedInterface) {
+ DexProgramClass ensureEmulateInterfaceLibrary(
+ DexProgramClass emulatedInterface, ProgramMethodSet synthesizedMethods) {
assert rewriter.isEmulatedInterface(emulatedInterface.type);
- DexProgramClass theProgramInterface = emulatedInterface.asProgramClass();
- DexProgramClass synthesizedClass = synthesizeEmulateInterfaceLibraryClass(theProgramInterface);
- assert synthesizedClass != null;
- syntheticClasses.put(emulatedInterface, synthesizedClass);
- }
-
- private DexProgramClass synthesizeEmulateInterfaceLibraryClass(DexProgramClass theInterface) {
- List<DexEncodedMethod> emulationMethods = new ArrayList<>();
- theInterface.forEachProgramMethodMatching(
- DexEncodedMethod::isDefaultMethod,
- method -> {
- DexMethod libraryMethod =
- method
- .getReference()
- .withHolder(emulatedInterfaces.get(theInterface.type), appView.dexItemFactory());
- DexMethod companionMethod =
- method.getAccessFlags().isStatic()
- ? rewriter.staticAsMethodOfCompanionClass(method)
- : rewriter.defaultAsMethodOfCompanionClass(method);
- // To properly emulate the library interface call, we need to compute the interfaces
- // inheriting from the interface and manually implement the dispatch with instance of.
- // The list guarantees that an interface is always after interfaces it extends,
- // hence reverse iteration.
- List<DexType> subInterfaces = emulatedInterfacesHierarchy.get(theInterface.type);
- List<Pair<DexType, DexMethod>> extraDispatchCases = new ArrayList<>();
- // In practice, there is usually a single case (except for tests),
- // so we do not bother to make the following loop more clever.
- Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
- appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember();
- for (DexString methodName : retargetCoreLibMember.keySet()) {
- if (method.getName() == methodName) {
- for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
- DexClass inClass = appView.definitionFor(inType);
- if (inClass != null && implementsInterface(inClass, theInterface.type)) {
- extraDispatchCases.add(
- new Pair<>(
- inType,
- appView
- .dexItemFactory()
- .createMethod(
- retargetCoreLibMember.get(methodName).get(inType),
- appView
- .dexItemFactory()
- .protoWithDifferentFirstParameter(
- companionMethod.proto, inType),
- method.getName())));
- }
- }
- }
- }
- if (subInterfaces != null) {
- for (int i = subInterfaces.size() - 1; i >= 0; i--) {
- DexClass subInterfaceClass = appView.definitionFor(subInterfaces.get(i));
- assert subInterfaceClass != null;
- // Else computation of subInterface would have failed.
- // if the method is implemented, extra dispatch is required.
- DexEncodedMethod result =
- subInterfaceClass.lookupVirtualMethod(method.getReference());
- if (result != null && !result.isAbstract()) {
- extraDispatchCases.add(
- new Pair<>(
- subInterfaceClass.type,
- appView
- .dexItemFactory()
- .createMethod(
- rewriter.getCompanionClassType(subInterfaceClass.type),
- appView
- .dexItemFactory()
- .protoWithDifferentFirstParameter(
- companionMethod.proto, subInterfaceClass.type),
- companionMethod.name)));
- }
- }
- }
- emulationMethods.add(
- DexEncodedMethod.toEmulateDispatchLibraryMethod(
- method.getHolderType(),
- rewriter.emulateInterfaceLibraryMethod(method),
- companionMethod,
- libraryMethod,
- extraDispatchCases,
- appView));
- });
- assert !emulationMethods.isEmpty();
DexType emulateLibraryClassType =
InterfaceMethodRewriter.getEmulateLibraryInterfaceClassType(
- theInterface.type, appView.dexItemFactory());
- ClassAccessFlags emulateLibraryClassFlags = theInterface.accessFlags.copy();
- emulateLibraryClassFlags.unsetAbstract();
- emulateLibraryClassFlags.unsetInterface();
- emulateLibraryClassFlags.unsetAnnotation();
- emulateLibraryClassFlags.setFinal();
- emulateLibraryClassFlags.setSynthetic();
- emulateLibraryClassFlags.setPublic();
- DexProgramClass clazz =
- new DexProgramClass(
- emulateLibraryClassType,
- null,
- new SynthesizedOrigin("interface desugaring (libs)", getClass()),
- emulateLibraryClassFlags,
- appView.dexItemFactory().objectType,
- DexTypeList.empty(),
- theInterface.sourceFile,
- null,
- Collections.emptyList(),
- null,
- Collections.emptyList(),
- ClassSignature.noSignature(),
- DexAnnotationSet.empty(),
- DexEncodedField.EMPTY_ARRAY,
- DexEncodedField.EMPTY_ARRAY,
- // All synthesized methods are static in this case.
- emulationMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
- DexEncodedMethod.EMPTY_ARRAY,
- appView.dexItemFactory().getSkipNameValidationForTesting(),
- DexProgramClass::checksumFromType);
- return clazz;
+ emulatedInterface.type, appView.dexItemFactory());
+ DexProgramClass emulateInterfaceClass =
+ appView
+ .getSyntheticItems()
+ .ensureFixedClassWhileMigrating(
+ SyntheticNaming.SyntheticKind.EMULATED_INTERFACE_CLASS,
+ emulateLibraryClassType,
+ emulatedInterface,
+ appView,
+ builder ->
+ emulatedInterface.forEachProgramMethodMatching(
+ DexEncodedMethod::isDefaultMethod,
+ method ->
+ builder.addMethod(
+ methodBuilder ->
+ synthesizeEmulatedInterfaceMethod(
+ method, emulatedInterface, methodBuilder))));
+ emulateInterfaceClass.forEachProgramMethod(synthesizedMethods::add);
+ return emulateInterfaceClass;
+ }
+
+ private void synthesizeEmulatedInterfaceMethod(
+ ProgramMethod method, DexProgramClass theInterface, SyntheticMethodBuilder methodBuilder) {
+ DexMethod libraryMethod =
+ method
+ .getReference()
+ .withHolder(emulatedInterfaces.get(theInterface.type), appView.dexItemFactory());
+ DexMethod companionMethod =
+ method.getAccessFlags().isStatic()
+ ? rewriter.staticAsMethodOfCompanionClass(method)
+ : rewriter.defaultAsMethodOfCompanionClass(method);
+ List<Pair<DexType, DexMethod>> extraDispatchCases =
+ getDispatchCases(method, theInterface, companionMethod);
+ DexMethod emulatedMethod = rewriter.emulateInterfaceLibraryMethod(method);
+ methodBuilder
+ .setName(emulatedMethod.getName())
+ .setProto(emulatedMethod.getProto())
+ .setCode(
+ theMethod ->
+ new EmulateInterfaceSyntheticCfCodeProvider(
+ theMethod.getHolderType(),
+ companionMethod,
+ libraryMethod,
+ extraDispatchCases,
+ appView)
+ .generateCfCode())
+ .setAccessFlags(
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_SYNTHETIC | Constants.ACC_STATIC | Constants.ACC_PUBLIC, false));
+ }
+
+ private List<Pair<DexType, DexMethod>> getDispatchCases(
+ ProgramMethod method, DexProgramClass theInterface, DexMethod companionMethod) {
+ // To properly emulate the library interface call, we need to compute the interfaces
+ // inheriting from the interface and manually implement the dispatch with instance of.
+ // The list guarantees that an interface is always after interfaces it extends,
+ // hence reverse iteration.
+ List<DexType> subInterfaces = emulatedInterfacesHierarchy.get(theInterface.type);
+ List<Pair<DexType, DexMethod>> extraDispatchCases = new ArrayList<>();
+ // In practice, there is usually a single case (except for tests),
+ // so we do not bother to make the following loop more clever.
+ Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
+ appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember();
+ for (DexString methodName : retargetCoreLibMember.keySet()) {
+ if (method.getName() == methodName) {
+ for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
+ DexClass inClass = appView.definitionFor(inType);
+ if (inClass != null && implementsInterface(inClass, theInterface.type)) {
+ extraDispatchCases.add(
+ new Pair<>(
+ inType,
+ appView
+ .dexItemFactory()
+ .createMethod(
+ retargetCoreLibMember.get(methodName).get(inType),
+ appView
+ .dexItemFactory()
+ .protoWithDifferentFirstParameter(companionMethod.proto, inType),
+ method.getName())));
+ }
+ }
+ }
+ }
+ if (subInterfaces != null) {
+ for (int i = subInterfaces.size() - 1; i >= 0; i--) {
+ DexClass subInterfaceClass = appView.definitionFor(subInterfaces.get(i));
+ assert subInterfaceClass != null;
+ // Else computation of subInterface would have failed.
+ // if the method is implemented, extra dispatch is required.
+ DexEncodedMethod result = subInterfaceClass.lookupVirtualMethod(method.getReference());
+ if (result != null && !result.isAbstract()) {
+ extraDispatchCases.add(
+ new Pair<>(
+ subInterfaceClass.type,
+ appView
+ .dexItemFactory()
+ .createMethod(
+ rewriter.getCompanionClassType(subInterfaceClass.type),
+ appView
+ .dexItemFactory()
+ .protoWithDifferentFirstParameter(
+ companionMethod.proto, subInterfaceClass.type),
+ companionMethod.name)));
+ }
+ }
+ }
+ return extraDispatchCases;
}
private boolean implementsInterface(DexClass clazz, DexType interfaceType) {
@@ -279,7 +271,7 @@
return;
}
if (needsEmulateInterfaceLibrary(emulatedInterface)) {
- generateEmulateInterfaceLibrary(emulatedInterface);
+ ensureEmulateInterfaceLibrary(emulatedInterface, synthesizedMethods);
}
}
@@ -291,7 +283,6 @@
public void finalizeProcessing(Builder<?> builder, ProgramMethodSet synthesizedMethods) {
warnMissingEmulatedInterfaces();
if (!appView.options().isDesugaredLibraryCompilation()) {
- assert syntheticClasses.isEmpty();
return;
}
for (DexType interfaceType : emulatedInterfaces.keySet()) {
@@ -304,12 +295,6 @@
}
}
}
- syntheticClasses.forEach(
- (interfaceClass, synthesizedClass) -> {
- builder.addSynthesizedClass(synthesizedClass);
- appView.appInfo().addSynthesizedClass(synthesizedClass, interfaceClass);
- synthesizedClass.forEachProgramMethod(synthesizedMethods::add);
- });
// TODO(b/183918843): Investigate what to do for the filtering, the minimum would be to make
// the rewriting rule explicit instead of using the synthesized class prefix.
filterEmulatedInterfaceSubInterfaces(builder);
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index 7bac324..8b19177 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -25,6 +25,7 @@
// Class synthetics.
RECORD_TAG("", false, true, true),
COMPANION_CLASS("-CC", false, true),
+ EMULATED_INTERFACE_CLASS("-EL", false, true),
LAMBDA("Lambda", false),
INIT_TYPE_ARGUMENT("-IA", false, true),
HORIZONTAL_INIT_TYPE_ARGUMENT_1(SYNTHETIC_CLASS_SEPARATOR + "IA$1", false, true),