Ensure default interface methods while desugaring.
Bug: 183998768
Change-Id: Id8846a4b299209de0d86779ef98bee3438559d9e
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 b2fe6cf..0dc90ad 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -222,7 +222,8 @@
assert defaultInterfaceMethodImplementation == null;
assert implementation != null;
assert code != null;
- assert code == implementation.getCode();
+ // TODO(b/183998768): Once R8 desugars in the enqueuer this should always be invalid code.
+ assert InvalidCode.isInvalidCode(code) || code == implementation.getCode();
accessFlags.setAbstract();
removeCode();
defaultInterfaceMethodImplementation = implementation;
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 3381ece..9ff7d2a 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
@@ -819,7 +819,7 @@
// In desugared library, emulated interface methods can be overridden by retarget lib members.
DexMethod forwardMethod =
target.getHolder().isInterface()
- ? rewriter.defaultAsMethodOfCompanionClass(target)
+ ? rewriter.ensureDefaultAsMethodOfCompanionClassStub(target).getReference()
: appView.options().desugaredLibraryConfiguration.retargetMethod(target, appView);
DexEncodedMethod desugaringForwardingMethod =
DexEncodedMethod.createDesugaringForwardingMethod(
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 30fc05c..398d304 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
@@ -5,7 +5,6 @@
import static com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.emulateInterfaceLibraryMethod;
-import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication.Builder;
import com.android.tools.r8.graph.DexClass;
@@ -163,20 +162,20 @@
private void synthesizeEmulatedInterfaceMethod(
ProgramMethod method, DexProgramClass theInterface, SyntheticMethodBuilder methodBuilder) {
+ assert !method.getDefinition().isStatic();
DexMethod libraryMethod =
method
.getReference()
.withHolder(emulatedInterfaces.get(theInterface.type), appView.dexItemFactory());
DexMethod companionMethod =
- method.getAccessFlags().isStatic()
- ? rewriter.staticAsMethodOfCompanionClass(method)
- : rewriter.defaultAsMethodOfCompanionClass(method);
+ rewriter.ensureDefaultAsMethodOfProgramCompanionClassStub(method).getReference();
List<Pair<DexType, DexMethod>> extraDispatchCases =
getDispatchCases(method, theInterface, companionMethod);
DexMethod emulatedMethod = emulateInterfaceLibraryMethod(method, appView.dexItemFactory());
methodBuilder
.setName(emulatedMethod.getName())
.setProto(emulatedMethod.getProto())
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
.setCode(
theMethod ->
new EmulateInterfaceSyntheticCfCodeProvider(
@@ -185,10 +184,7 @@
libraryMethod,
extraDispatchCases,
appView)
- .generateCfCode())
- .setAccessFlags(
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_SYNTHETIC | Constants.ACC_STATIC | Constants.ACC_PUBLIC, false));
+ .generateCfCode());
}
private List<Pair<DexType, DexMethod>> getDispatchCases(
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 2bd6a48..1681a83 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
@@ -38,6 +38,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.InvalidCode;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
@@ -744,7 +745,7 @@
// TODO(b/183998768): Why does this not create a stub on the class path?
: privateAsMethodOfCompanionClass(directTarget);
} else {
- companionMethod = defaultAsMethodOfCompanionClass(directTarget);
+ companionMethod = ensureDefaultAsMethodOfCompanionClassStub(directTarget).getReference();
}
return rewriteInvoke.apply(companionMethod);
} else {
@@ -754,7 +755,8 @@
if (virtualTarget != null) {
// This is a invoke-direct call to a virtual method.
assert invokeNeedsRewriting(invokedMethod, DIRECT);
- return rewriteInvoke.apply(defaultAsMethodOfCompanionClass(virtualTarget));
+ return rewriteInvoke.apply(
+ ensureDefaultAsMethodOfCompanionClassStub(virtualTarget).getReference());
} else {
// The below assert is here because a well-type program should have a target, but we
// cannot throw a compilation error, since we have no knowledge about the input.
@@ -925,7 +927,8 @@
DexClass holder = target.getHolder();
if (holder.isLibraryClass() && holder.isInterface()) {
assert invokeNeedsRewriting(invokedMethod, SUPER);
- return rewriteInvoke.apply(defaultAsMethodOfCompanionClass(target));
+ return rewriteInvoke.apply(
+ ensureDefaultAsMethodOfCompanionClassStub(target).getReference());
}
}
}
@@ -938,25 +941,23 @@
DexClassAndMethod superTarget =
appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
if (superTarget != null && superTarget.isLibraryMethod()) {
+ assert invokeNeedsRewriting(invokedMethod, SUPER);
// Rewriting is required because the super invoke resolves into a missing
// method (method is on desugared library). Find out if it needs to be
- // retarget or if it just calls a companion class method and rewrite.
+ // retargeted or if it just calls a companion class method and rewrite.
DexMethod retargetMethod =
options.desugaredLibraryConfiguration.retargetMethod(superTarget, appView);
- if (retargetMethod == null) {
- DexMethod originalCompanionMethod = defaultAsMethodOfCompanionClass(superTarget);
- DexMethod companionMethod =
- factory.createMethod(
- getCompanionClassType(emulatedItf),
- factory.protoWithDifferentFirstParameter(
- originalCompanionMethod.proto, emulatedItf),
- originalCompanionMethod.name);
- assert invokeNeedsRewriting(invokedMethod, SUPER);
- return rewriteInvoke.apply(companionMethod);
- } else {
- assert invokeNeedsRewriting(invokedMethod, SUPER);
+ if (retargetMethod != null) {
return rewriteInvoke.apply(retargetMethod);
}
+ DexClassAndMethod emulatedMethod =
+ superTarget.getReference().lookupMemberOnClass(appView.definitionFor(emulatedItf));
+ if (emulatedMethod == null) {
+ assert false;
+ return null;
+ }
+ DexClassAndMethod companionMethod = ensureDefaultAsMethodOfCompanionClassStub(emulatedMethod);
+ return rewriteInvoke.apply(companionMethod.getReference());
}
return null;
}
@@ -1194,6 +1195,16 @@
return factory.createType(interfaceTypeDescriptor);
}
+ DexClassAndMethod ensureDefaultAsMethodOfCompanionClassStub(DexClassAndMethod method) {
+ if (method.isProgramMethod()) {
+ return ensureDefaultAsMethodOfProgramCompanionClassStub(method.asProgramMethod());
+ }
+ ClasspathOrLibraryClass context = method.getHolder().asClasspathOrLibraryClass();
+ DexMethod companionMethodReference =
+ defaultAsMethodOfCompanionClass(method.getReference(), appView.dexItemFactory());
+ return ensureMethodOfClasspathCompanionClassStub(companionMethodReference, context, appView);
+ }
+
DexClassAndMethod ensureStaticAsMethodOfCompanionClassStub(DexClassAndMethod method) {
if (method.isProgramMethod()) {
return ensureStaticAsMethodOfProgramCompanionClassStub(method.asProgramMethod());
@@ -1204,6 +1215,37 @@
}
}
+ ProgramMethod ensureDefaultAsMethodOfProgramCompanionClassStub(ProgramMethod method) {
+ DexEncodedMethod virtual = method.getDefinition();
+ DexMethod companionMethod =
+ defaultAsMethodOfCompanionClass(method.getReference(), appView.dexItemFactory());
+ return InterfaceProcessor.ensureCompanionMethod(
+ method.getHolder(),
+ companionMethod.getName(),
+ companionMethod.getProto(),
+ appView,
+ methodBuilder -> {
+ MethodAccessFlags newFlags = method.getAccessFlags().copy();
+ newFlags.promoteToStatic();
+ methodBuilder
+ .setAccessFlags(newFlags)
+ .setGenericSignature(MethodTypeSignature.noSignature())
+ .setAnnotations(
+ virtual
+ .annotations()
+ .methodParametersWithFakeThisArguments(appView.dexItemFactory()))
+ .setParameterAnnotationsList(
+ virtual.getParameterAnnotations().withFakeThisParameter())
+ // TODO(b/183998768): Once R8 desugars in the enqueuer this should set an invalid
+ // code to ensure it is never used before desugared and installed.
+ .setCode(
+ ignored ->
+ appView.enableWholeProgramOptimizations()
+ ? virtual.getCode()
+ : InvalidCode.getInstance());
+ });
+ }
+
ProgramMethod ensurePrivateAsMethodOfProgramCompanionClassStub(ProgramMethod method) {
DexMethod companionMethod =
privateAsMethodOfCompanionClass(method.getReference(), appView.dexItemFactory());
@@ -1222,6 +1264,7 @@
.setAccessFlags(newFlags)
.setGenericSignature(definition.getGenericSignature())
.setAnnotations(definition.annotations())
+ // TODO(b/183998768): Should this not also be updating with a fake 'this'
.setParameterAnnotationsList(definition.getParameterAnnotations())
// TODO(b/183998768): Once R8 desugars in the enqueuer this should set an invalid
// code to ensure it is never used before desugared and installed.
@@ -1272,13 +1315,6 @@
return instanceAsMethodOfCompanionClass(method, DEFAULT_METHOD_PREFIX, factory);
}
- public final DexMethod defaultAsMethodOfCompanionClass(DexClassAndMethod method) {
- DexItemFactory dexItemFactory = appView.dexItemFactory();
- DexMethod rewritten = defaultAsMethodOfCompanionClass(method.getReference(), dexItemFactory);
- recordCompanionClassReference(appView, method, rewritten);
- return rewritten;
- }
-
// Represent a private instance interface method as a method of companion class.
static DexMethod privateAsMethodOfCompanionClass(DexMethod method, DexItemFactory factory) {
// Add an implicit argument to represent the receiver.
@@ -1289,17 +1325,6 @@
return privateAsMethodOfCompanionClass(method.getReference(), factory);
}
- private static DexClassAndMethod recordCompanionClassReference(
- AppView<?> appView, DexClassAndMethod method, DexMethod rewritten) {
- ClasspathOrLibraryClass context = method.getHolder().asClasspathOrLibraryClass();
- // If the interface class is a program class, we shouldn't need to synthesize the companion
- // class on the classpath.
- if (context == null) {
- return null;
- }
- return ensureMethodOfClasspathCompanionClassStub(rewritten, context, appView);
- }
-
private static DexClassAndMethod ensureMethodOfClasspathCompanionClassStub(
DexMethod companionMethodReference, ClasspathOrLibraryClass context, AppView<?> appView) {
return appView
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 cd4e9cb..64775fb 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
@@ -34,7 +34,6 @@
import com.android.tools.r8.graph.FieldAccessFlags;
import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InvalidCode;
import com.android.tools.r8.graph.MethodAccessFlags;
@@ -232,44 +231,19 @@
+ method.toSourceString(),
iface.origin);
}
-
- // Create a new method in a companion class to represent default method implementation.
- DexMethod companionMethod = rewriter.defaultAsMethodOfCompanionClass(method);
-
Code code = virtual.getCode();
if (code == null) {
throw new CompilationError(
"Code is missing for default " + "interface method: " + method.toSourceString(),
iface.origin);
}
-
- MethodAccessFlags newFlags = method.getAccessFlags().copy();
- newFlags.promoteToStatic();
+ // Create a new method in a companion class to represent default method implementation.
+ ProgramMethod companion = rewriter.ensureDefaultAsMethodOfProgramCompanionClassStub(method);
DexEncodedMethod.setDebugInfoWithFakeThisParameter(
- code, companionMethod.getArity(), appView);
-
- ensureCompanionMethod(
- iface,
- companionMethod.getName(),
- companionMethod.getProto(),
- appView,
- methodBuilder ->
- methodBuilder
- .setAccessFlags(newFlags)
- .setGenericSignature(MethodTypeSignature.noSignature())
- .setAnnotations(
- virtual
- .annotations()
- .methodParametersWithFakeThisArguments(appView.dexItemFactory()))
- .setParameterAnnotationsList(
- virtual.getParameterAnnotations().withFakeThisParameter())
- .setCode(ignored -> virtual.getCode())
- .setOnBuildConsumer(
- implMethod -> {
- implMethod.copyMetadata(virtual);
- getPostProcessingInterfaceInfo(iface)
- .mapDefaultMethodToCompanionMethod(virtual, implMethod);
- }));
+ code, companion.getReference().getArity(), appView);
+ finalizeMoveToCompanionMethod(method, companion);
+ getPostProcessingInterfaceInfo(iface)
+ .mapDefaultMethodToCompanionMethod(virtual, companion.getDefinition());
}
}
}
@@ -300,7 +274,6 @@
companion = rewriter.ensureStaticAsMethodOfProgramCompanionClassStub(method);
} else {
assert definition.isPrivate();
- companion = rewriter.ensurePrivateAsMethodOfProgramCompanionClassStub(method);
Code code = definition.getCode();
if (code == null) {
throw new CompilationError(
@@ -309,24 +282,31 @@
+ method.getReference().toSourceString(),
iface.origin);
}
+ companion = rewriter.ensurePrivateAsMethodOfProgramCompanionClassStub(method);
DexEncodedMethod.setDebugInfoWithFakeThisParameter(
code, companion.getReference().getArity(), appView);
}
- // TODO(b/183998768): R8 should also install an "invalid code" object until the actual code
- // moves.
- assert appView.enableWholeProgramOptimizations()
- || InvalidCode.isInvalidCode(companion.getDefinition().getCode());
- if (definition.hasClassFileVersion()) {
- companion.getDefinition().downgradeClassFileVersion(definition.getClassFileVersion());
- }
- companion.getDefinition().setCode(definition.getCode(), appView);
+ finalizeMoveToCompanionMethod(method, companion);
getPostProcessingInterfaceInfo(iface)
.moveMethod(method.getReference(), companion.getReference());
- definition.setCode(InvalidCode.getInstance(), appView);
}
}
+ private void finalizeMoveToCompanionMethod(ProgramMethod method, ProgramMethod companion) {
+ // TODO(b/183998768): R8 should also install an "invalid code" object until the actual code
+ // moves.
+ assert appView.enableWholeProgramOptimizations()
+ || InvalidCode.isInvalidCode(companion.getDefinition().getCode());
+ DexProgramClass iface = method.getHolder();
+ DexEncodedMethod definition = method.getDefinition();
+ if (definition.hasClassFileVersion()) {
+ companion.getDefinition().downgradeClassFileVersion(definition.getClassFileVersion());
+ }
+ companion.getDefinition().setCode(definition.getCode(), appView);
+ definition.setCode(InvalidCode.getInstance(), appView);
+ }
+
private void clearDirectMethods(DexProgramClass iface) {
DexEncodedMethod clinit = iface.getClassInitializer();
MethodCollection methodCollection = iface.getMethodCollection();