Synthesize companion classpath classes expected to be present
Change-Id: Icfbea5d176adba2fa6d0c1b1b19d30d34bdcf86b
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 1df9806..dd88661 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1003,6 +1003,10 @@
}
public CfCode buildEmptyThrowingCfCode() {
+ return buildEmptyThrowingCfCode(method);
+ }
+
+ public static CfCode buildEmptyThrowingCfCode(DexMethod method) {
CfInstruction insn[] = {new CfConstNull(), new CfThrow()};
return new CfCode(
method.holder,
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 1d66acd..618ff5b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -212,6 +212,15 @@
return Iterables.transform(directMethods(predicate), method -> new ProgramMethod(this, method));
}
+ public Iterable<ProgramMethod> virtualProgramMethods() {
+ return Iterables.transform(virtualMethods(), method -> new ProgramMethod(this, method));
+ }
+
+ public Iterable<ProgramMethod> virtualProgramMethods(Predicate<DexEncodedMethod> predicate) {
+ return Iterables.transform(
+ virtualMethods(predicate), method -> new ProgramMethod(this, method));
+ }
+
public Iterable<ProgramMethod> programInstanceInitializers() {
return directProgramMethods(DexEncodedMethod::isInstanceInitializer);
}
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 8682669..bd6d488 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
@@ -23,6 +23,7 @@
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
@@ -35,7 +36,10 @@
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.GenericSignature;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.MethodCollection;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -63,6 +67,7 @@
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.shaking.MainDexClasses;
import com.android.tools.r8.synthesis.SyntheticNaming;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IterableUtils;
@@ -355,16 +360,16 @@
getMethodOrigin(context.getReference()));
}
- DexEncodedMethod directTarget = clazz.lookupMethod(method);
+ DexClassAndMethod directTarget = clazz.lookupClassMethod(method);
if (directTarget != null) {
// This can be a private instance method call. Note that the referenced
// method is expected to be in the current class since it is private, but desugaring
// may move some methods or their code into other classes.
instructions.replaceCurrentInstruction(
new InvokeStatic(
- directTarget.isPrivateMethod()
- ? privateAsMethodOfCompanionClass(method)
- : defaultAsMethodOfCompanionClass(method),
+ directTarget.getDefinition().isPrivateMethod()
+ ? privateAsMethodOfCompanionClass(directTarget)
+ : defaultAsMethodOfCompanionClass(directTarget),
invoke.outValue(),
invoke.arguments()));
} else {
@@ -375,7 +380,7 @@
// This is a invoke-direct call to a virtual method.
instructions.replaceCurrentInstruction(
new InvokeStatic(
- defaultAsMethodOfCompanionClass(virtualTarget.getDefinition().method),
+ defaultAsMethodOfCompanionClass(virtualTarget),
invoke.outValue(),
invoke.arguments()));
} else {
@@ -490,6 +495,7 @@
UtilityMethodsForCodeOptimizations.synthesizeThrowNoSuchMethodErrorMethod(
appView, context, methodProcessingId);
throwNoSuchMethodErrorMethod.optimize(methodProcessor);
+
InvokeStatic throwNoSuchMethodErrorInvoke =
InvokeStatic.builder()
.setMethod(throwNoSuchMethodErrorMethod.getMethod())
@@ -533,7 +539,7 @@
DexMethod amendedMethod = amendDefaultMethod(context.getHolder(), invokedMethod);
instructions.replaceCurrentInstruction(
new InvokeStatic(
- defaultAsMethodOfCompanionClass(amendedMethod),
+ defaultAsMethodOfCompanionClass(amendedMethod, appView.dexItemFactory()),
invoke.outValue(),
invoke.arguments()));
} else {
@@ -547,7 +553,7 @@
if (holder.isLibraryClass() && holder.isInterface()) {
instructions.replaceCurrentInstruction(
new InvokeStatic(
- defaultAsMethodOfCompanionClass(target.getReference(), factory),
+ defaultAsMethodOfCompanionClass(target),
invoke.outValue(),
invoke.arguments()));
}
@@ -567,9 +573,7 @@
DexMethod retargetMethod =
options.desugaredLibraryConfiguration.retargetMethod(superTarget, appView);
if (retargetMethod == null) {
- DexMethod originalCompanionMethod =
- instanceAsMethodOfCompanionClass(
- superTarget.getReference(), DEFAULT_METHOD_PREFIX, factory);
+ DexMethod originalCompanionMethod = defaultAsMethodOfCompanionClass(superTarget);
DexMethod companionMethod =
factory.createMethod(
getCompanionClassType(emulatedItf),
@@ -955,12 +959,11 @@
// Represent a static interface method as a method of companion class.
final DexMethod staticAsMethodOfCompanionClass(DexClassAndMethod method) {
- return staticAsMethodOfCompanionClass(method.getReference());
- }
-
- final DexMethod staticAsMethodOfCompanionClass(DexMethod method) {
- // No changes for static methods.
- return factory.createMethod(getCompanionClassType(method.holder), method.proto, method.name);
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ DexType companionClassType = getCompanionClassType(method.getHolderType(), dexItemFactory);
+ DexMethod rewritten = method.getReference().withHolder(companionClassType, dexItemFactory);
+ recordCompanionClassReference(appView, method, rewritten);
+ return rewritten;
}
private static DexMethod instanceAsMethodOfCompanionClass(
@@ -993,12 +996,11 @@
return instanceAsMethodOfCompanionClass(method, DEFAULT_METHOD_PREFIX, factory);
}
- DexMethod defaultAsMethodOfCompanionClass(DexMethod method) {
- return defaultAsMethodOfCompanionClass(method, factory);
- }
-
- DexMethod defaultAsMethodOfCompanionClass(DexClassAndMethod method) {
- return defaultAsMethodOfCompanionClass(method.getReference(), factory);
+ 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.
@@ -1007,8 +1009,57 @@
return instanceAsMethodOfCompanionClass(method, PRIVATE_METHOD_PREFIX, factory);
}
- DexMethod privateAsMethodOfCompanionClass(DexMethod method) {
- return privateAsMethodOfCompanionClass(method, factory);
+ private DexMethod privateAsMethodOfCompanionClass(DexClassAndMethod method) {
+ return privateAsMethodOfCompanionClass(method.getReference(), factory);
+ }
+
+ private static void recordCompanionClassReference(
+ AppView<?> appView, DexClassAndMethod method, DexMethod rewritten) {
+ // If the interface class is a program class, we shouldn't need to synthesize the companion
+ // class on the classpath.
+ if (method.getHolder().isProgramClass()) {
+ return;
+ }
+
+ // If the companion class does not exist, then synthesize it on the classpath.
+ DexClass companionClass = appView.definitionFor(rewritten.getHolderType());
+ if (companionClass == null) {
+ companionClass =
+ synthesizeEmptyCompanionClass(appView, rewritten.getHolderType(), method.getHolder());
+ }
+
+ // If the companion class is a classpath class, then synthesize the companion class method if it
+ // does not exist.
+ if (companionClass.isClasspathClass()) {
+ synthetizeCompanionClassMethodIfNotPresent(companionClass.asClasspathClass(), rewritten);
+ }
+ }
+
+ private static DexClasspathClass synthesizeEmptyCompanionClass(
+ AppView<?> appView, DexType type, DexClass context) {
+ return appView
+ .getSyntheticItems()
+ .createClasspathClass(
+ SyntheticKind.COMPANION_CLASS, type, context, appView.dexItemFactory());
+ }
+
+ private static void synthetizeCompanionClassMethodIfNotPresent(
+ DexClasspathClass companionClass, DexMethod method) {
+ MethodCollection methodCollection = companionClass.getMethodCollection();
+ synchronized (methodCollection) {
+ if (methodCollection.getMethod(method) == null) {
+ boolean d8R8Synthesized = true;
+ methodCollection.addDirectMethod(
+ new DexEncodedMethod(
+ method,
+ MethodAccessFlags.createPublicStaticSynthetic(),
+ MethodTypeSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ ParameterAnnotationsList.empty(),
+ DexEncodedMethod.buildEmptyThrowingCfCode(method),
+ d8R8Synthesized));
+ }
+ }
}
private void renameEmulatedInterfaces() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index c2db31a..e40251c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -220,23 +220,28 @@
List<DexEncodedMethod> companionMethods,
InterfaceProcessorNestedGraphLens.Builder graphLensBuilder) {
List<DexEncodedMethod> newVirtualMethods = new ArrayList<>();
- for (DexEncodedMethod virtual : iface.virtualMethods()) {
+ for (ProgramMethod method : iface.virtualProgramMethods()) {
+ DexEncodedMethod virtual = method.getDefinition();
if (rewriter.isDefaultMethod(virtual)) {
if (!canMoveToCompanionClass(virtual)) {
- throw new CompilationError("One or more instruction is preventing default interface "
- + "method from being desugared: " + virtual.method.toSourceString(), iface.origin);
+ throw new CompilationError(
+ "One or more instruction is preventing default interface "
+ + "method from being desugared: "
+ + method.toSourceString(),
+ iface.origin);
}
// Create a new method in a companion class to represent default method implementation.
- DexMethod companionMethod = rewriter.defaultAsMethodOfCompanionClass(virtual.method);
+ DexMethod companionMethod = rewriter.defaultAsMethodOfCompanionClass(method);
Code code = virtual.getCode();
if (code == null) {
- throw new CompilationError("Code is missing for default "
- + "interface method: " + virtual.method.toSourceString(), iface.origin);
+ throw new CompilationError(
+ "Code is missing for default " + "interface method: " + method.toSourceString(),
+ iface.origin);
}
- MethodAccessFlags newFlags = virtual.accessFlags.copy();
+ MethodAccessFlags newFlags = method.getAccessFlags().copy();
newFlags.promoteToStatic();
DexEncodedMethod.setDebugInfoWithFakeThisParameter(
code, companionMethod.getArity(), appView);
@@ -252,7 +257,7 @@
implMethod.copyMetadata(virtual);
virtual.setDefaultInterfaceMethodImplementation(implMethod);
companionMethods.add(implMethod);
- graphLensBuilder.recordCodeMovedToCompanionClass(virtual.method, implMethod.method);
+ graphLensBuilder.recordCodeMovedToCompanionClass(method.getReference(), implMethod.method);
}
// Remove bridge methods.
@@ -272,12 +277,13 @@
List<DexEncodedMethod> companionMethods,
InterfaceProcessorNestedGraphLens.Builder graphLensBuilder) {
DexEncodedMethod clinit = null;
- for (DexEncodedMethod method : iface.directMethods()) {
- if (method.isClassInitializer()) {
- clinit = method;
+ for (ProgramMethod method : iface.directProgramMethods()) {
+ DexEncodedMethod definition = method.getDefinition();
+ if (definition.isClassInitializer()) {
+ clinit = definition;
continue;
}
- if (method.isInstanceInitializer()) {
+ if (definition.isInstanceInitializer()) {
assert false
: "Unexpected interface instance initializer: "
+ method.getReference().toSourceString();
@@ -291,24 +297,24 @@
}
DexMethod oldMethod = method.getReference();
- if (isStaticMethod(method)) {
+ if (isStaticMethod(definition)) {
assert originalFlags.isPrivate() || originalFlags.isPublic()
: "Static interface method "
+ method.toSourceString()
+ " is expected to "
+ "either be public or private in "
+ iface.origin;
- DexMethod companionMethod = rewriter.staticAsMethodOfCompanionClass(oldMethod);
+ DexMethod companionMethod = rewriter.staticAsMethodOfCompanionClass(method);
DexEncodedMethod implMethod =
new DexEncodedMethod(
companionMethod,
newFlags,
- method.getGenericSignature(),
- method.annotations(),
- method.parameterAnnotationsList,
- method.getCode(),
+ definition.getGenericSignature(),
+ definition.annotations(),
+ definition.parameterAnnotationsList,
+ definition.getCode(),
true);
- implMethod.copyMetadata(method);
+ implMethod.copyMetadata(definition);
companionMethods.add(implMethod);
graphLensBuilder.move(oldMethod, companionMethod);
continue;
@@ -318,9 +324,10 @@
newFlags.promoteToStatic();
- DexMethod companionMethod = rewriter.privateAsMethodOfCompanionClass(oldMethod);
+ DexMethod companionMethod =
+ rewriter.privateAsMethodOfCompanionClass(oldMethod, appView.dexItemFactory());
- Code code = method.getCode();
+ Code code = definition.getCode();
if (code == null) {
throw new CompilationError(
"Code is missing for private instance "
@@ -333,12 +340,12 @@
new DexEncodedMethod(
companionMethod,
newFlags,
- method.getGenericSignature(),
- method.annotations(),
- method.parameterAnnotationsList,
+ definition.getGenericSignature(),
+ definition.annotations(),
+ definition.parameterAnnotationsList,
code,
true);
- implMethod.copyMetadata(method);
+ implMethod.copyMetadata(definition);
companionMethods.add(implMethod);
graphLensBuilder.move(oldMethod, companionMethod);
}