Ensure static methods while desugaring.
Bug: 183998768
Change-Id: Ia2e1ebb7c2bc51c27d040549781af2cf167bea45
diff --git a/src/main/java/com/android/tools/r8/graph/InvalidCode.java b/src/main/java/com/android/tools/r8/graph/InvalidCode.java
new file mode 100644
index 0000000..418de86
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/InvalidCode.java
@@ -0,0 +1,69 @@
+// 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.graph;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.origin.Origin;
+
+public class InvalidCode extends Code {
+
+ private static final InvalidCode INSTANCE = new InvalidCode();
+
+ public static Code getInstance() {
+ return INSTANCE;
+ }
+
+ public static boolean isInvalidCode(Code code) {
+ return code == INSTANCE;
+ }
+
+ private InvalidCode() {}
+
+ @Override
+ public IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public void registerCodeReferences(ProgramMethod method, UseRegistry registry) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public void registerCodeReferencesForDesugaring(ClasspathMethod method, UseRegistry registry) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public String toString() {
+ return "<invalid-code>";
+ }
+
+ @Override
+ public String toString(DexEncodedMethod method, ClassNameMapper naming) {
+ return toString();
+ }
+
+ @Override
+ public int estimatedDexCodeSizeUpperBoundInBytes() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public boolean isEmptyVoidMethod() {
+ throw new Unreachable();
+ }
+
+ @Override
+ protected int computeHashCode() {
+ return System.identityHashCode(this);
+ }
+
+ @Override
+ protected boolean computeEquals(Object other) {
+ return this == other;
+ }
+}
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 79afbf8..ae334dc 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
@@ -40,6 +40,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.InvalidCode;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
@@ -858,9 +859,9 @@
assert resolutionResult != null;
assert resolutionResult.getResolvedMethod().isStatic();
assert invokeNeedsRewriting(invokedMethod, STATIC);
-
- return rewriteInvoke.apply(
- staticAsMethodOfCompanionClass(resolutionResult.getResolutionPair()));
+ DexClassAndMethod companionMethod =
+ ensureStaticAsMethodOfCompanionClassStub(resolutionResult.getResolutionPair());
+ return rewriteInvoke.apply(companionMethod.getReference());
}
private Collection<CfInstruction> rewriteInvokeSuper(
@@ -1187,12 +1188,21 @@
return factory.createType(interfaceTypeDescriptor);
}
+ DexClassAndMethod ensureStaticAsMethodOfCompanionClassStub(DexClassAndMethod method) {
+ if (method.isProgramMethod()) {
+ return ensureStaticAsMethodOfProgramCompanionClassStub(method.asProgramMethod());
+ } else {
+ ClasspathOrLibraryClass context = method.getHolder().asClasspathOrLibraryClass();
+ DexMethod companionMethodReference = staticAsMethodOfCompanionClass(method);
+ return ensureMethodOfClasspathCompanionClassStub(companionMethodReference, context, appView);
+ }
+ }
+
// Represent a static interface method as a method of companion class.
final DexMethod staticAsMethodOfCompanionClass(DexClassAndMethod method) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
DexType companionClassType = getCompanionClassType(method.getHolderType(), dexItemFactory);
DexMethod rewritten = method.getReference().withHolder(companionClassType, dexItemFactory);
- recordCompanionClassReference(appView, method, rewritten);
return rewritten;
}
@@ -1244,19 +1254,24 @@
return privateAsMethodOfCompanionClass(method.getReference(), factory);
}
- private static void recordCompanionClassReference(
+ 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;
+ return null;
}
- appView
+ return ensureMethodOfClasspathCompanionClassStub(rewritten, context, appView);
+ }
+
+ private static DexClassAndMethod ensureMethodOfClasspathCompanionClassStub(
+ DexMethod companionMethodReference, ClasspathOrLibraryClass context, AppView<?> appView) {
+ return appView
.getSyntheticItems()
.ensureFixedClasspathClassMethod(
- rewritten.getName(),
- rewritten.getProto(),
+ companionMethodReference.getName(),
+ companionMethodReference.getProto(),
SyntheticKind.COMPANION_CLASS,
context,
appView,
@@ -1267,6 +1282,32 @@
.setCode(DexEncodedMethod::buildEmptyThrowingCfCode));
}
+ ProgramMethod ensureStaticAsMethodOfProgramCompanionClassStub(ProgramMethod method) {
+ DexMethod companionMethodReference = staticAsMethodOfCompanionClass(method);
+ DexEncodedMethod definition = method.getDefinition();
+ return InterfaceProcessor.ensureCompanionMethod(
+ method.getHolder(),
+ companionMethodReference.getName(),
+ companionMethodReference.getProto(),
+ appView,
+ methodBuilder -> {
+ MethodAccessFlags newFlags = definition.getAccessFlags().copy();
+ newFlags.promoteToPublic();
+ methodBuilder
+ .setAccessFlags(newFlags)
+ .setGenericSignature(definition.getGenericSignature())
+ .setAnnotations(definition.annotations())
+ .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.
+ .setCode(
+ m ->
+ appView.enableWholeProgramOptimizations()
+ ? definition.getCode()
+ : InvalidCode.getInstance());
+ });
+ }
+
/**
* Move static and default interface methods to companion classes, add missing methods to forward
* to moved default methods implementation.
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 0eebbdb..ab61be0 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
@@ -36,6 +36,7 @@
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;
import com.android.tools.r8.graph.MethodCollection;
import com.android.tools.r8.graph.NestedGraphLens;
@@ -74,12 +75,10 @@
private final InterfaceMethodRewriter rewriter;
private final Map<DexProgramClass, PostProcessingInterfaceInfo> postProcessingInterfaceInfos =
new ConcurrentHashMap<>();
- private final ClassTypeSignature objectTypeSignature;
InterfaceProcessor(AppView<?> appView, InterfaceMethodRewriter rewriter) {
this.appView = appView;
this.rewriter = rewriter;
- this.objectTypeSignature = new ClassTypeSignature(appView.dexItemFactory().objectType);
}
@Override
@@ -111,30 +110,29 @@
processDirectInterfaceMethods(iface);
}
- private ProgramMethod ensureCompanionMethod(
+ static ProgramMethod ensureCompanionMethod(
DexProgramClass iface,
DexString methodName,
DexProto methodProto,
+ AppView<?> appView,
Consumer<SyntheticMethodBuilder> fn) {
- ProgramMethod method =
- appView
- .getSyntheticItems()
- .ensureFixedClassMethod(
- methodName,
- methodProto,
- SyntheticKind.COMPANION_CLASS,
- iface,
- appView,
- builder ->
- builder
- .setSourceFile(iface.sourceFile)
- .setGenericSignature(
- iface
- .getClassSignature()
- .toObjectBoundWithSameFormals(objectTypeSignature)),
- fn);
- assert method.getHolderType() == rewriter.getCompanionClassType(iface.type);
- return method;
+ return appView
+ .getSyntheticItems()
+ .ensureFixedClassMethod(
+ methodName,
+ methodProto,
+ SyntheticKind.COMPANION_CLASS,
+ iface,
+ appView,
+ builder ->
+ builder
+ .setSourceFile(iface.sourceFile)
+ .setGenericSignature(
+ iface
+ .getClassSignature()
+ .toObjectBoundWithSameFormals(
+ new ClassTypeSignature(appView.dexItemFactory().objectType))),
+ fn);
}
private void ensureCompanionClassInitializesInterface(
@@ -148,6 +146,7 @@
iface,
appView.dexItemFactory().classConstructorMethodName,
appView.dexItemFactory().createProto(appView.dexItemFactory().voidType),
+ appView,
methodBuilder -> createCompanionClassInitializer(iface, clinitField, methodBuilder));
synthesizedMethods.add(clinit);
}
@@ -252,6 +251,7 @@
iface,
companionMethod.getName(),
companionMethod.getProto(),
+ appView,
methodBuilder ->
methodBuilder
.setAccessFlags(newFlags)
@@ -302,25 +302,17 @@
+ " is expected to "
+ "either be public or private in "
+ iface.origin;
- DexMethod companionMethod = rewriter.staticAsMethodOfCompanionClass(method);
-
- ensureCompanionMethod(
- iface,
- companionMethod.getName(),
- companionMethod.getProto(),
- methodBuilder ->
- methodBuilder
- .setAccessFlags(newFlags)
- .setGenericSignature(definition.getGenericSignature())
- .setAnnotations(definition.annotations())
- .setParameterAnnotationsList(definition.getParameterAnnotations())
- .setCode(ignored -> definition.getCode())
- .setOnBuildConsumer(
- implMethod -> {
- implMethod.copyMetadata(definition);
- }));
-
- getPostProcessingInterfaceInfo(iface).moveMethod(oldMethod, companionMethod);
+ ProgramMethod companion = rewriter.ensureStaticAsMethodOfProgramCompanionClassStub(method);
+ // 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);
+ getPostProcessingInterfaceInfo(iface).moveMethod(oldMethod, companion.getReference());
+ definition.setCode(InvalidCode.getInstance(), appView);
continue;
}
@@ -347,6 +339,7 @@
iface,
companionMethod.getName(),
companionMethod.getProto(),
+ appView,
methodBuilder ->
methodBuilder
.setAccessFlags(newFlags)
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index 608f635..eebc9fe 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -168,7 +168,7 @@
.withBuilderTransformation(ToolHelper::allowTestProguardOptions)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 7, "lambdadesugaringnplus"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 6, "lambdadesugaringnplus"))
.run();
test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
@@ -177,7 +177,7 @@
.withBuilderTransformation(ToolHelper::allowTestProguardOptions)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 4, "lambdadesugaringnplus"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaringnplus"))
.run();
}