Move interface method desugarings to the R8 enqueuer.
Bug: 183998768
Bug: 197613376
Fixes: 167345026
Fixes: 167535447
Fixes: 187377562
Fixes: 168697955
Fixes: 159987443
Fixes: 196345511
Fixes: 197851381
Change-Id: I5e605297e24aa20dbc04547a60807a93f646a630
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 0243703..6cbb223 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -166,8 +166,6 @@
/** Generic signature information if the attribute is present in the input */
private MethodTypeSignature genericSignature;
- private DexEncodedMethod defaultInterfaceMethodImplementation = null;
-
private OptionalBool isLibraryMethodOverride = OptionalBool.unknown();
private Int2ReferenceMap<DebugLocalInfo> parameterInfo = NO_PARAMETER_INFO;
@@ -211,21 +209,6 @@
return compilationState;
}
- public DexEncodedMethod getDefaultInterfaceMethodImplementation() {
- return defaultInterfaceMethodImplementation;
- }
-
- public void setDefaultInterfaceMethodImplementation(DexEncodedMethod implementation) {
- assert defaultInterfaceMethodImplementation == null;
- assert implementation != null;
- assert code != null;
- // 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;
- }
-
/**
* Flags this method as no longer being obsolete.
*
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index 37dea58..a81d742 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
-import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor.InterfaceProcessorNestedGraphLens;
import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
import com.android.tools.r8.optimize.MemberRebindingLens;
import com.android.tools.r8.shaking.KeepInfoCollection;
@@ -480,14 +479,6 @@
return null;
}
- public boolean isInterfaceProcessorLens() {
- return false;
- }
-
- public InterfaceProcessorNestedGraphLens asInterfaceProcessorLens() {
- return null;
- }
-
public GraphLens withCodeRewritingsApplied(DexItemFactory dexItemFactory) {
if (hasCodeRewritings()) {
return new ClearCodeRewritingGraphLens(dexItemFactory, this);
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
index 7f09192..459c008 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
@@ -534,7 +534,8 @@
private boolean verifyAllSuperTypesAreInHierarchy(
DexDefinitionSupplier definitions, Iterable<DexType> dexTypes) {
for (DexType supertype : dexTypes) {
- assert typeIsInHierarchy(definitions, supertype);
+ assert typeIsInHierarchy(definitions, supertype)
+ : "Type not found in hierarchy: " + supertype;
}
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index c00f43c..8f6f293 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.conversion;
import static com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor.ExcludeDexResources;
-import static com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor.IncludeAllResources;
import static com.android.tools.r8.ir.desugar.lambda.D8LambdaDesugaring.rewriteEnclosingLambdaMethodAttributes;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
@@ -58,7 +57,6 @@
import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceApplicationRewriter;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
-import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor;
import com.android.tools.r8.ir.desugar.lambda.LambdaDeserializationMethodRemover;
import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
import com.android.tools.r8.ir.optimize.AssertionsRewriter;
@@ -362,23 +360,6 @@
}
}
- private void finalizeInterfaceMethodRewritingThroughIR(ExecutorService executorService)
- throws ExecutionException {
- assert !appView.getSyntheticItems().hasPendingSyntheticClasses();
- if (interfaceMethodRewriter != null) {
- interfaceMethodRewriter.finalizeInterfaceMethodRewritingThroughIR(this, executorService);
- }
- }
-
- private void runInterfaceDesugaringProcessorsForR8(
- Flavor includeAllResources, ExecutorService executorService) throws ExecutionException {
- assert !appView.getSyntheticItems().hasPendingSyntheticClasses();
- if (interfaceMethodRewriter != null) {
- interfaceMethodRewriter.runInterfaceDesugaringProcessorsForR8(
- this, includeAllResources, executorService);
- }
- }
-
private void processCovariantReturnTypeAnnotations(Builder<?> builder) {
if (covariantReturnTypeAnnotationTransformer != null) {
covariantReturnTypeAnnotationTransformer.process(builder);
@@ -460,10 +441,11 @@
CfPostProcessingDesugaringEventConsumer.createForD8(methodProcessor, instructionDesugaring);
methodProcessor.newWave();
InterfaceMethodProcessorFacade interfaceDesugaring =
- instructionDesugaring.getInterfaceMethodPostProcessingDesugaring(ExcludeDexResources);
+ instructionDesugaring.getInterfaceMethodPostProcessingDesugaringD8(ExcludeDexResources);
CfPostProcessingDesugaringCollection.create(
appView, interfaceDesugaring, instructionDesugaring.getRetargetingInfo())
- .postProcessingDesugaring(appView.appInfo().classes(), eventConsumer, executorService);
+ .postProcessingDesugaring(
+ appView.appInfo().classes(), m -> true, eventConsumer, executorService);
methodProcessor.awaitMethodProcessing();
eventConsumer.finalizeDesugaring();
}
@@ -789,11 +771,6 @@
Builder<?> builder = appView.appInfo().app().builder();
builder.setHighestSortingString(highestSortingString);
- printPhase("Interface method desugaring");
- finalizeInterfaceMethodRewritingThroughIR(executorService);
- runInterfaceDesugaringProcessorsForR8(IncludeAllResources, executorService);
- feedback.updateVisibleOptimizationInfo();
-
if (serviceLoaderRewriter != null) {
processSynthesizedServiceLoaderMethods(
serviceLoaderRewriter.getServiceLoadMethods(), executorService);
@@ -1466,14 +1443,6 @@
previous = printMethod(code, "IR after class inlining (SSA)", previous);
- // TODO(b/183998768): Enable interface method rewriter cf to cf also in R8.
- if (interfaceMethodRewriter != null && appView.enableWholeProgramOptimizations()) {
- timing.begin("Rewrite interface methods");
- interfaceMethodRewriter.rewriteMethodReferences(
- code, methodProcessor, methodProcessingContext);
- timing.end();
- }
-
assert code.verifyTypes(appView);
previous = printMethod(code, "IR after interface method rewriting (SSA)", previous);
@@ -1504,12 +1473,15 @@
timing.begin("Canonicalize constants");
constantCanonicalizer.canonicalize(appView, code);
timing.end();
+ previous = printMethod(code, "IR after constant canonicalization (SSA)", previous);
timing.begin("Create constants for literal instructions");
codeRewriter.useDedicatedConstantForLitInstruction(code);
timing.end();
+ previous = printMethod(code, "IR after constant literals (SSA)", previous);
timing.begin("Shorten live ranges");
codeRewriter.shortenLiveRanges(code);
timing.end();
+ previous = printMethod(code, "IR after shorten live ranges (SSA)", previous);
}
timing.begin("Canonicalize idempotent calls");
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
index 10032bc..5d4f7b6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
@@ -11,9 +11,11 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor;
+import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor;
import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
import com.android.tools.r8.utils.ThrowingConsumer;
import java.util.function.Consumer;
+import java.util.function.Predicate;
/**
* Abstracts a collection of low-level desugarings (i.e., mappings from class-file instructions to
@@ -60,9 +62,12 @@
public abstract <T extends Throwable> void withD8NestBasedAccessDesugaring(
ThrowingConsumer<D8NestBasedAccessDesugaring, T> consumer) throws T;
- public abstract InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaring(
+ public abstract InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaringD8(
Flavor flavor);
+ public abstract InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaringR8(
+ Flavor flavor, Predicate<ProgramMethod> isLiveMethod, InterfaceProcessor processor);
+
public abstract RetargetingInfo getRetargetingInfo();
public abstract void withDesugaredLibraryAPIConverter(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index 4f6dd6c..c1dccc3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -62,15 +62,29 @@
AppView<? extends AppInfoWithClassHierarchy> appView,
BiConsumer<LambdaClass, ProgramMethod> lambdaClassConsumer,
BiConsumer<ProgramMethod, ProgramMethod> twrCloseResourceMethodConsumer,
- SyntheticAdditions additions) {
+ SyntheticAdditions additions,
+ BiConsumer<ProgramMethod, ProgramMethod> companionMethodConsumer) {
return new R8CfInstructionDesugaringEventConsumer(
- appView, lambdaClassConsumer, twrCloseResourceMethodConsumer, additions);
+ appView,
+ lambdaClassConsumer,
+ twrCloseResourceMethodConsumer,
+ additions,
+ companionMethodConsumer);
}
+ // TODO(b/183998768): Remove this event consumer. It should be unneeded for R8 and for D8 the
+ // desugaring of interface methods should be able to happen up front too avoiding the companion
+ // callback on nest accessors.
public static CfInstructionDesugaringEventConsumer createForDesugaredCode() {
return new CfInstructionDesugaringEventConsumer() {
@Override
+ public void acceptCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) {
+ // A synthesized nest based accessor may itself be defined on an interface, in which case
+ // desugaring the accessor will result in a rewrite to the companion method.
+ }
+
+ @Override
public void acceptClasspathEmulatedInterface(DexClasspathClass clazz) {
assert false;
}
@@ -167,6 +181,11 @@
}
@Override
+ public void acceptCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) {
+ // Intentionally empty. Methods are moved when processing the interface definition.
+ }
+
+ @Override
public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) {
// Intentionally empty.
}
@@ -321,15 +340,24 @@
new IdentityHashMap<>();
private final List<InvokeSpecialBridgeInfo> pendingInvokeSpecialBridges = new ArrayList<>();
+ private final BiConsumer<ProgramMethod, ProgramMethod> onCompanionMethodCallback;
+
public R8CfInstructionDesugaringEventConsumer(
AppView<? extends AppInfoWithClassHierarchy> appView,
BiConsumer<LambdaClass, ProgramMethod> lambdaClassConsumer,
BiConsumer<ProgramMethod, ProgramMethod> twrCloseResourceMethodConsumer,
- SyntheticAdditions additions) {
+ SyntheticAdditions additions,
+ BiConsumer<ProgramMethod, ProgramMethod> onCompanionMethodCallback) {
this.appView = appView;
this.lambdaClassConsumer = lambdaClassConsumer;
this.twrCloseResourceMethodConsumer = twrCloseResourceMethodConsumer;
this.additions = additions;
+ this.onCompanionMethodCallback = onCompanionMethodCallback;
+ }
+
+ @Override
+ public void acceptCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) {
+ onCompanionMethodCallback.accept(method, companionMethod);
}
@Override
@@ -344,7 +372,7 @@
@Override
public void acceptCompanionClassClinit(ProgramMethod method) {
- // TODO(b/183998768): Update this once desugaring is moved to the enqueuer.
+ // Intentionally empty. The method will be hit by tracing if required.
}
@Override
@@ -354,19 +382,18 @@
@Override
public void acceptRecordMethod(ProgramMethod method) {
- // Intentionally empty. The method will be hit by the tracing in R8 as if it was
- // present in the input code, and thus nothing needs to be done.
+ // Intentionally empty. The method will be hit by tracing if required.
}
@Override
public void acceptThrowMethod(ProgramMethod method, ProgramMethod context) {
- assert false : "TODO(b/183998768): To be implemented";
+ // Intentionally empty. The method will be hit by tracing if required.
}
@Override
public void acceptInvokeStaticInterfaceOutliningMethod(
ProgramMethod method, ProgramMethod context) {
- assert false : "TODO(b/183998768): To be implemented";
+ // Intentionally empty. The method will be hit by tracing if required.
}
@Override
@@ -376,14 +403,12 @@
@Override
public void acceptAPIConversion(ProgramMethod method) {
- // Intentionally empty. The method will be hit by the tracing in R8 as if it was
- // present in the input code, and thus nothing needs to be done.
+ // Intentionally empty. The method will be hit by tracing if required.
}
@Override
public void acceptBackportedMethod(ProgramMethod backportedMethod, ProgramMethod context) {
- // Intentionally empty. The backported method will be hit by the tracing in R8 as if it was
- // present in the input code, and thus nothing needs to be done.
+ // Intentionally empty. The method will be hit by tracing if required.
}
@Override
@@ -420,8 +445,6 @@
@Override
public void acceptTwrCloseResourceMethod(ProgramMethod closeMethod, ProgramMethod context) {
- // Intentionally empty. The close method will be hit by the tracing in R8 as if they were
- // present in the input code, and thus nothing needs to be done.
// TODO(b/180091213): Remove the recording of the synthesizing context when this is accessible
// from synthetic items.
twrCloseResourceMethodConsumer.accept(closeMethod, context);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
index dbba296..a0d130d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPICallbackSynthesizor;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterPostProcessor;
import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo;
@@ -15,6 +16,7 @@
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+import java.util.function.Predicate;
public abstract class CfPostProcessingDesugaringCollection {
@@ -35,6 +37,7 @@
public abstract void postProcessingDesugaring(
Collection<DexProgramClass> programClasses,
+ Predicate<ProgramMethod> isLiveMethod,
CfPostProcessingDesugaringEventConsumer eventConsumer,
ExecutorService executorService)
throws ExecutionException;
@@ -83,6 +86,7 @@
@Override
public void postProcessingDesugaring(
Collection<DexProgramClass> programClasses,
+ Predicate<ProgramMethod> isLiveMethod,
CfPostProcessingDesugaringEventConsumer eventConsumer,
ExecutorService executorService)
throws ExecutionException {
@@ -107,6 +111,7 @@
@Override
public void postProcessingDesugaring(
Collection<DexProgramClass> programClasses,
+ Predicate<ProgramMethod> isLiveMethod,
CfPostProcessingDesugaringEventConsumer eventConsumer,
ExecutorService executorService)
throws ExecutionException {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
index 66042a0..e4a34c4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
@@ -30,8 +30,9 @@
return new D8CfPostProcessingDesugaringEventConsumer(methodProcessor, instructionDesugaring);
}
- public static R8PostProcessingDesugaringEventConsumer createForR8(SyntheticAdditions additions) {
- return new R8PostProcessingDesugaringEventConsumer(additions);
+ public static R8PostProcessingDesugaringEventConsumer createForR8(
+ SyntheticAdditions additions, CfInstructionDesugaringCollection desugaring) {
+ return new R8PostProcessingDesugaringEventConsumer(additions, desugaring);
}
public abstract void finalizeDesugaring() throws ExecutionException;
@@ -53,10 +54,8 @@
}
private void addMethodToReprocess(ProgramMethod method) {
- if (instructionDesugaring.needsDesugaring(method)) {
- instructionDesugaring.needsDesugaring(method);
- }
assert !instructionDesugaring.needsDesugaring(method);
+ assert method.getDefinition().getCode().isCfCode();
methodsToReprocess.add(method);
}
@@ -71,13 +70,24 @@
}
@Override
+ public void acceptEmulatedInterfaceMarkerInterface(
+ DexProgramClass clazz, DexClasspathClass newInterface) {
+ // Intentionally empty.
+ }
+
+ @Override
public void acceptForwardingMethod(ProgramMethod method) {
addMethodToReprocess(method);
}
@Override
public void acceptCompanionClassClinit(ProgramMethod method) {
- methodsToReprocess.add(method);
+ addMethodToReprocess(method);
+ }
+
+ @Override
+ public void acceptCompanionMethod(ProgramMethod method, ProgramMethod companion) {
+ // Intentionally empty. The method must be processed on the interface definition.
}
@Override
@@ -103,17 +113,27 @@
extends CfPostProcessingDesugaringEventConsumer {
private final SyntheticAdditions additions;
+ private final CfInstructionDesugaringCollection desugaring;
- R8PostProcessingDesugaringEventConsumer(SyntheticAdditions additions) {
+ R8PostProcessingDesugaringEventConsumer(
+ SyntheticAdditions additions, CfInstructionDesugaringCollection desugaring) {
this.additions = additions;
+ this.desugaring = desugaring;
}
@Override
- public void finalizeDesugaring() throws ExecutionException {
+ public void finalizeDesugaring() {
// Intentionally empty.
}
@Override
+ public void acceptEmulatedInterfaceMarkerInterface(
+ DexProgramClass clazz, DexClasspathClass newInterface) {
+ additions.injectInterface(clazz, newInterface);
+ additions.addLiveClasspathClass(newInterface);
+ }
+
+ @Override
public void acceptInterfaceInjection(DexProgramClass clazz, DexClass newInterface) {
additions.injectInterface(clazz, newInterface);
}
@@ -130,11 +150,19 @@
@Override
public void acceptCompanionClassClinit(ProgramMethod method) {
- assert false : "TODO(b/183998768): Support Interface processing in R8";
+ // Generation of this method must have been done during enqueuing.
+ assert false;
+ }
+
+ @Override
+ public void acceptCompanionMethod(ProgramMethod method, ProgramMethod companion) {
+ // Generation of this method must have been done during enqueuing.
+ assert false;
}
@Override
public void acceptAPIConversionCallback(ProgramMethod method) {
+ assert !desugaring.needsDesugaring(method);
additions.addLiveMethod(method);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
index b23f31e..027b27c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
@@ -10,9 +10,11 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor;
+import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor;
import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
import com.android.tools.r8.utils.ThrowingConsumer;
import java.util.function.Consumer;
+import java.util.function.Predicate;
public class EmptyCfInstructionDesugaringCollection extends CfInstructionDesugaringCollection {
@@ -61,7 +63,14 @@
}
@Override
- public InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaring(Flavor flavor) {
+ public InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaringD8(
+ Flavor flavor) {
+ return null;
+ }
+
+ @Override
+ public InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaringR8(
+ Flavor flavor, Predicate<ProgramMethod> isLiveMethod, InterfaceProcessor processor) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index b117828..dd15950 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor;
+import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor;
import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring;
import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring;
@@ -35,6 +36,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
+import java.util.function.Predicate;
public class NonEmptyCfInstructionDesugaringCollection extends CfInstructionDesugaringCollection {
@@ -73,9 +75,7 @@
if (appView.options().enableTryWithResourcesDesugaring()) {
desugarings.add(new TwrInstructionDesugaring(appView));
}
- // TODO(b/183998768): Enable interface method rewriter cf to cf also in R8.
- if (appView.options().isInterfaceMethodDesugaringEnabled()
- && !appView.enableWholeProgramOptimizations()) {
+ if (appView.options().isInterfaceMethodDesugaringEnabled()) {
interfaceMethodRewriter =
new InterfaceMethodRewriter(
appView, backportedMethodRewriter, desugaredLibraryRetargeter);
@@ -83,18 +83,11 @@
} else {
interfaceMethodRewriter = null;
}
- // In R8 interface method rewriting is performed in IR, we still need to filter
- // out from API conversion methods desugared by the interface method rewriter.
- InterfaceMethodRewriter enforcedInterfaceMethodRewriter =
- interfaceMethodRewriter == null && appView.options().isInterfaceMethodDesugaringEnabled()
- ? new InterfaceMethodRewriter(
- appView, backportedMethodRewriter, desugaredLibraryRetargeter)
- : interfaceMethodRewriter;
desugaredLibraryAPIConverter =
appView.rewritePrefix.isRewriting()
? new DesugaredLibraryAPIConverter(
appView,
- enforcedInterfaceMethodRewriter,
+ interfaceMethodRewriter,
desugaredLibraryRetargeter,
backportedMethodRewriter)
: null;
@@ -182,7 +175,7 @@
IntBox maxStackForInstruction = new IntBox(cfCode.getMaxStack());
List<CfInstruction> desugaredInstructions =
- ListUtils.flatMap(
+ ListUtils.flatMapSameType(
cfCode.getInstructions(),
instruction -> {
Collection<CfInstruction> replacement =
@@ -305,7 +298,7 @@
assert !alsoApplicable
|| (appliedDesugaring instanceof InterfaceMethodRewriter
&& (desugaring instanceof InvokeToPrivateRewriter
- || desugaring instanceof D8NestBasedAccessDesugaring))
+ || desugaring instanceof NestBasedAccessDesugaring))
|| (appliedDesugaring instanceof TwrInstructionDesugaring
&& desugaring instanceof InterfaceMethodRewriter)
: "Desugaring of "
@@ -330,9 +323,18 @@
}
@Override
- public InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaring(Flavor flavor) {
+ public InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaringD8(
+ Flavor flavor) {
return interfaceMethodRewriter != null
- ? interfaceMethodRewriter.getPostProcessingDesugaring(flavor)
+ ? interfaceMethodRewriter.getPostProcessingDesugaringD8(flavor)
+ : null;
+ }
+
+ @Override
+ public InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaringR8(
+ Flavor flavor, Predicate<ProgramMethod> isLiveMethod, InterfaceProcessor processor) {
+ return interfaceMethodRewriter != null
+ ? interfaceMethodRewriter.getPostProcessingDesugaringR8(flavor, isLiveMethod, processor)
: null;
}
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 90a7b35..8661587 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
@@ -24,16 +24,19 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GenericSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.LibraryMethod;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ParameterAnnotationsList;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.WorkList;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.base.Equivalence.Wrapper;
@@ -50,6 +53,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.function.Predicate;
import org.objectweb.asm.Opcodes;
/**
@@ -344,6 +348,7 @@
private final InterfaceDesugaringSyntheticHelper helper;
private final MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
private final boolean needsLibraryInfo;
+ private final Predicate<ProgramMethod> isLiveMethod;
// Mapping from program and classpath classes to their information summary.
private final Map<DexClass, ClassInfo> classInfo = new ConcurrentHashMap<>();
@@ -358,7 +363,11 @@
private final Map<DexProgramClass, ProgramMethodSet> newSyntheticMethods =
new ConcurrentHashMap<>();
- ClassProcessor(AppView<?> appView) {
+ // Mapping from actual program classes to the extra interfaces needed for emulated dispatch.
+ private final Map<DexProgramClass, List<ClassTypeSignature>> newExtraInterfaceSignatures =
+ new ConcurrentHashMap<>();
+
+ ClassProcessor(AppView<?> appView, Predicate<ProgramMethod> isLiveMethod) {
this.appView = appView;
this.dexItemFactory = appView.dexItemFactory();
this.helper = new InterfaceDesugaringSyntheticHelper(appView);
@@ -369,6 +378,14 @@
.desugaredLibraryConfiguration
.getRetargetCoreLibMember()
.isEmpty();
+ this.isLiveMethod = isLiveMethod;
+ }
+
+ private boolean isLiveMethod(DexClassAndMethod method) {
+ if (method.isProgramMethod()) {
+ return isLiveMethod.test(method.asProgramMethod());
+ }
+ return true;
}
private boolean needsLibraryInfo() {
@@ -396,6 +413,16 @@
clazz.addVirtualMethods(newForwardingMethods.toDefinitionSet());
newForwardingMethods.forEach(eventConsumer::acceptForwardingMethod);
});
+ newExtraInterfaceSignatures.forEach(
+ (clazz, extraInterfaceSignatures) -> {
+ if (!extraInterfaceSignatures.isEmpty()) {
+ for (ClassTypeSignature signature : extraInterfaceSignatures) {
+ eventConsumer.acceptEmulatedInterfaceMarkerInterface(
+ clazz, helper.ensureEmulatedInterfaceMarkerInterface(signature.type()));
+ }
+ clazz.addExtraInterfaces(extraInterfaceSignatures);
+ }
+ });
}
// Computes the set of method signatures that may need forwarding methods on derived classes.
@@ -533,7 +560,7 @@
}
}
});
- clazz.asProgramClass().addExtraInterfaces(extraInterfaceSignatures);
+ newExtraInterfaceSignatures.put(clazz.asProgramClass(), extraInterfaceSignatures);
}
private void collectEmulatedInterfaces(
@@ -637,7 +664,7 @@
clazz,
wrapper.get(),
target -> {
- if (!superInfo.isTargetedByForwards(target)) {
+ if (isLiveMethod(target) && !superInfo.isTargetedByForwards(target)) {
additionalForwards.add(target);
addForwardingMethod(target, clazz);
}
@@ -821,6 +848,10 @@
DexEncodedMethod desugaringForwardingMethod =
DexEncodedMethod.createDesugaringForwardingMethod(
target, clazz, forwardMethod, dexItemFactory);
+ if (!target.isProgramDefinition()
+ || target.getDefinition().isLibraryMethodOverride().isTrue()) {
+ desugaringForwardingMethod.setLibraryMethodOverride(OptionalBool.TRUE);
+ }
addSyntheticMethod(clazz.asProgramClass(), desugaringForwardingMethod);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
index 92a64fe..9ee3606 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.graph.DexAnnotationSet;
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.DexField;
@@ -32,7 +33,9 @@
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.ir.desugar.CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceSynthesizerEventConsumer.ClasspathEmulatedInterfaceSynthesizerEventConsumer;
+import com.android.tools.r8.synthesis.SyntheticClassBuilder;
import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.InternalOptions;
@@ -41,7 +44,6 @@
import com.google.common.collect.ImmutableList;
import java.util.Map;
import java.util.Set;
-import java.util.function.Consumer;
import java.util.function.Predicate;
import org.objectweb.asm.Opcodes;
@@ -185,6 +187,17 @@
return factory.createType(interfaceTypeDescriptor);
}
+ // TODO(b/198273164): This should take the context class and not just a type.
+ DexClasspathClass ensureEmulatedInterfaceMarkerInterface(DexType type) {
+ return appView
+ .getSyntheticItems()
+ .ensureFixedClasspathClassFromType(
+ SyntheticKind.EMULATED_INTERFACE_MARKER_CLASS,
+ type,
+ appView,
+ SyntheticClassBuilder::setInterface);
+ }
+
DexClassAndMethod ensureEmulatedInterfaceMethod(
DexClassAndMethod method, ClasspathEmulatedInterfaceSynthesizerEventConsumer eventConsumer) {
DexMethod emulatedInterfaceMethod = emulateInterfaceLibraryMethod(method);
@@ -232,10 +245,10 @@
}
DexClassAndMethod ensureStaticAsMethodOfCompanionClassStub(
- DexClassAndMethod method, Consumer<ProgramMethod> companionClinitConsumer) {
+ DexClassAndMethod method, CfInstructionDesugaringEventConsumer eventConsumer) {
if (method.isProgramMethod()) {
return ensureStaticAsMethodOfProgramCompanionClassStub(
- method.asProgramMethod(), companionClinitConsumer);
+ method.asProgramMethod(), eventConsumer);
} else {
ClasspathOrLibraryClass context = method.getHolder().asClasspathOrLibraryClass();
DexMethod companionMethodReference = staticAsMethodOfCompanionClass(method);
@@ -264,15 +277,7 @@
.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(
- syntheticMethod ->
- appView.enableWholeProgramOptimizations()
- ? virtual
- .getCode()
- .getCodeAsInlining(syntheticMethod, method.getReference())
- : InvalidCode.getInstance());
+ .setCode(ignored -> InvalidCode.getInstance());
},
ignored -> {});
}
@@ -297,15 +302,7 @@
.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.
- .setCode(
- syntheticMethod ->
- appView.enableWholeProgramOptimizations()
- ? definition
- .getCode()
- .getCodeAsInlining(syntheticMethod, method.getReference())
- : InvalidCode.getInstance());
+ .setCode(ignored -> InvalidCode.getInstance());
},
ignored -> {});
}
@@ -368,9 +365,10 @@
}
ProgramMethod ensureStaticAsMethodOfProgramCompanionClassStub(
- ProgramMethod method, Consumer<ProgramMethod> companionClinitConsumer) {
- if (!method.getDefinition().isClassInitializer() && method.getHolder().hasClassInitializer()) {
- ensureCompanionClassInitializesInterface(method.getHolder(), companionClinitConsumer);
+ ProgramMethod method, InterfaceMethodDesugaringBaseEventConsumer eventConsumer) {
+ assert !method.getDefinition().isClassInitializer();
+ if (method.getHolder().hasClassInitializer()) {
+ ensureCompanionClassInitializesInterface(method.getHolder(), eventConsumer);
}
DexMethod companionMethodReference = staticAsMethodOfCompanionClass(method);
DexEncodedMethod definition = method.getDefinition();
@@ -387,21 +385,29 @@
.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(
- syntheticMethod ->
- appView.enableWholeProgramOptimizations()
- ? definition
- .getCode()
- .getCodeAsInlining(syntheticMethod, method.getReference())
- : InvalidCode.getInstance());
+ .setCode(ignored -> InvalidCode.getInstance());
},
- ignored -> {});
+ companion -> eventConsumer.acceptCompanionMethod(method, companion));
+ }
+
+ public ProgramMethod ensureMethodOfProgramCompanionClassStub(
+ ProgramMethod method, InterfaceMethodDesugaringBaseEventConsumer eventConsumer) {
+ DexEncodedMethod definition = method.getDefinition();
+ assert method.getHolder().isInterface();
+ assert definition.isNonAbstractNonNativeMethod();
+ assert definition.getCode() != null;
+ assert !InvalidCode.isInvalidCode(definition.getCode());
+ if (definition.isStatic()) {
+ return ensureStaticAsMethodOfProgramCompanionClassStub(method, eventConsumer);
+ }
+ if (definition.isPrivate()) {
+ return ensurePrivateAsMethodOfProgramCompanionClassStub(method);
+ }
+ return ensureDefaultAsMethodOfProgramCompanionClassStub(method);
}
private void ensureCompanionClassInitializesInterface(
- DexProgramClass iface, Consumer<ProgramMethod> companionClinitConsumer) {
+ DexProgramClass iface, InterfaceMethodDesugaringBaseEventConsumer eventConsumer) {
assert hasStaticMethodThatTriggersNonTrivialClassInitializer(iface);
InterfaceProcessor.ensureCompanionMethod(
iface,
@@ -409,7 +415,7 @@
appView.dexItemFactory().createProto(appView.dexItemFactory().voidType),
appView,
methodBuilder -> createCompanionClassInitializer(iface, methodBuilder),
- companionClinitConsumer);
+ eventConsumer::acceptCompanionClassClinit);
}
private DexEncodedField ensureStaticClinitFieldToTriggerInterfaceInitialization(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringBaseEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringBaseEventConsumer.java
new file mode 100644
index 0000000..4e1a30f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringBaseEventConsumer.java
@@ -0,0 +1,13 @@
+// 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.ProgramMethod;
+
+public interface InterfaceMethodDesugaringBaseEventConsumer {
+
+ void acceptCompanionClassClinit(ProgramMethod method);
+
+ void acceptCompanionMethod(ProgramMethod method, ProgramMethod companionMethod);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringEventConsumer.java
index bdc8816..9a65214 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringEventConsumer.java
@@ -6,13 +6,10 @@
import com.android.tools.r8.graph.ProgramMethod;
-public interface InterfaceMethodDesugaringEventConsumer {
+public interface InterfaceMethodDesugaringEventConsumer
+ extends InterfaceMethodDesugaringBaseEventConsumer {
void acceptThrowMethod(ProgramMethod method, ProgramMethod context);
void acceptInvokeStaticInterfaceOutliningMethod(ProgramMethod method, ProgramMethod context);
-
- void acceptCompanionClassClinit(ProgramMethod method);
-
- // TODO(b/183998768): Add acceptCompanionClass and acceptEmulatedInterface.
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
index f989858..94a4e60 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
@@ -7,18 +7,17 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaring;
import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor;
import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+import java.util.function.Predicate;
public class InterfaceMethodProcessorFacade implements CfPostProcessingDesugaring {
@@ -26,18 +25,30 @@
private final Flavor flavour;
private final List<InterfaceDesugaringProcessor> interfaceDesugaringProcessors;
- InterfaceMethodProcessorFacade(AppView<?> appView, Flavor flavour) {
+ InterfaceMethodProcessorFacade(
+ AppView<?> appView, Flavor flavour, Predicate<ProgramMethod> isLiveMethod) {
this.appView = appView;
this.flavour = flavour;
- interfaceDesugaringProcessors = instantiateInterfaceDesugaringProcessors(appView);
+ interfaceDesugaringProcessors = instantiateInterfaceDesugaringProcessors(appView, isLiveMethod);
+ }
+
+ InterfaceMethodProcessorFacade(
+ AppView<?> appView,
+ Flavor flavour,
+ Predicate<ProgramMethod> isLiveMethod,
+ InterfaceProcessor interfaceProcessor) {
+ this.appView = appView;
+ this.flavour = flavour;
+ interfaceDesugaringProcessors =
+ ImmutableList.of(new ClassProcessor(appView, isLiveMethod), interfaceProcessor);
}
private List<InterfaceDesugaringProcessor> instantiateInterfaceDesugaringProcessors(
- AppView<?> appView) {
+ AppView<?> appView, Predicate<ProgramMethod> isLiveMethod) {
// Process all classes first. Add missing forwarding methods to
// replace desugared default interface methods.
- ClassProcessor classProcessor = new ClassProcessor(appView);
+ ClassProcessor classProcessor = new ClassProcessor(appView, isLiveMethod);
// Process interfaces, create companion or dispatch class if needed, move static
// methods to companion class, copy default interface methods to companion classes,
@@ -49,39 +60,6 @@
return ImmutableList.of(classProcessor, interfaceProcessor);
}
- /** Runs the interfaceProcessor, the class processor and the emulated interface processor. */
- void runInterfaceDesugaringProcessorsForR8(IRConverter converter, ExecutorService executorService)
- throws ExecutionException {
-
- CollectingInterfaceDesugaringEventConsumer eventConsumer =
- new CollectingInterfaceDesugaringEventConsumer();
- processClassesConcurrently(appView.appInfo().classes(), eventConsumer, executorService);
- converter.processMethodsConcurrently(
- eventConsumer.getSortedSynthesizedMethods(), executorService);
- }
-
- // This temporary class avoids the duality between collecting with IR processing and
- // having events with the Cf desugaring.
- private static class CollectingInterfaceDesugaringEventConsumer
- implements InterfaceProcessingDesugaringEventConsumer {
-
- SortedProgramMethodSet sortedSynthesizedMethods = SortedProgramMethodSet.createConcurrent();
-
- @Override
- public void acceptForwardingMethod(ProgramMethod method) {
- sortedSynthesizedMethods.add(method);
- }
-
- @Override
- public void acceptCompanionClassClinit(ProgramMethod method) {
- sortedSynthesizedMethods.add(method);
- }
-
- public SortedProgramMethodSet getSortedSynthesizedMethods() {
- return sortedSynthesizedMethods;
- }
- }
-
private boolean shouldProcess(DexProgramClass clazz, Flavor flavour) {
if (appView.isAlreadyLibraryDesugared(clazz)) {
return false;
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 8b3f70e..77f2c98 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,10 +38,8 @@
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.code.InvokeMethod;
@@ -70,7 +68,6 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IteratorUtils;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
-import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import com.android.tools.r8.utils.structural.Ordered;
import com.google.common.collect.Sets;
import java.util.ArrayList;
@@ -80,11 +77,10 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.function.Predicate;
//
// Default and static interface method desugaring rewriter (note that lambda
@@ -267,7 +263,9 @@
AppInfoWithClassHierarchy appInfoForDesugaring = appView.appInfoForDesugaring();
SingleResolutionResult resolution =
appInfoForDesugaring.resolveMethod(method, invokeType == INTERFACE).asSingleResolution();
- if (resolution != null && resolution.getResolvedMethod().isPrivateMethod()) {
+ if (resolution != null
+ && (resolution.getResolvedMethod().isPrivate()
+ || resolution.getResolvedMethod().isStatic())) {
return true;
}
return defaultMethodForEmulatedDispatchOrNull(method, invokeType == INTERFACE) != null;
@@ -442,10 +440,15 @@
.resolveMethod(invoke.getMethod(), invoke.isInterface())
.asSingleResolution();
if (resolution != null
- && resolution.getResolvedMethod().isPrivateMethod()
+ && resolution.getResolvedMethod().isPrivate()
&& resolution.isAccessibleFrom(context, appInfoForDesugaring).isTrue()) {
- return rewriteInvokeDirect(invoke.getMethod(), context, rewriteInvoke);
+ // TODO(b/198267586): What about the private in-accessible case?
+ return rewriteInvokeDirect(invoke.getMethod(), context, rewriteInvoke, eventConsumer);
}
+ if (resolution != null && resolution.getResolvedMethod().isStatic()) {
+ return rewriteToThrow.apply(resolution);
+ }
+ // TODO(b/198267586): What about an invoke <init>?
return rewriteInvokeInterfaceOrInvokeVirtual(
invoke.getMethod(), invoke.isInterface(), rewriteInvoke, eventConsumer);
}
@@ -465,13 +468,14 @@
staticOutliningMethodConsumer,
rewriteInvoke,
rewriteToThrow,
- eventConsumer::acceptCompanionClassClinit);
+ eventConsumer);
}
assert invoke.isInvokeSpecial();
if (invoke.isInvokeSuper(context.getHolderType())) {
- return rewriteInvokeSuper(invoke.getMethod(), context, rewriteInvoke, rewriteToThrow);
+ return rewriteInvokeSuper(
+ invoke.getMethod(), context, rewriteInvoke, rewriteToThrow, eventConsumer);
}
- return rewriteInvokeDirect(invoke.getMethod(), context, rewriteInvoke);
+ return rewriteInvokeDirect(invoke.getMethod(), context, rewriteInvoke, eventConsumer);
}
private Collection<CfInstruction> rewriteInvokeToThrowCf(
@@ -568,113 +572,6 @@
}
}
- // Rewrites the references to static and default interface methods.
- // NOTE: can be called for different methods concurrently.
- public void rewriteMethodReferences(
- IRCode code,
- MethodProcessor methodProcessor,
- MethodProcessingContext methodProcessingContext) {
- ProgramMethod context = code.context();
- if (isSyntheticMethodThatShouldNotBeDoubleProcessed(code.context())) {
- // As the synthetics for dispatching to static interface methods are not desugared again
- // this can leave a static invoke to a static method on an interface.
- leavingStaticInvokeToInterface(context.asProgramMethod());
- return;
- }
-
- Set<Value> affectedValues = Sets.newIdentityHashSet();
- Set<BasicBlock> blocksToRemove = Sets.newIdentityHashSet();
- ListIterator<BasicBlock> blocks = code.listIterator();
- while (blocks.hasNext()) {
- BasicBlock block = blocks.next();
- if (blocksToRemove.contains(block)) {
- continue;
- }
- InstructionListIterator instructions = block.listIterator(code);
- while (instructions.hasNext()) {
- Instruction instruction = instructions.next();
- rewriteMethodReferences(
- code,
- methodProcessor,
- methodProcessingContext,
- context,
- affectedValues,
- blocksToRemove,
- blocks,
- instructions,
- instruction);
- }
- }
-
- code.removeBlocks(blocksToRemove);
-
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
-
- assert code.isConsistentSSA();
- }
-
- private void rewriteMethodReferences(
- IRCode code,
- MethodProcessor methodProcessor,
- MethodProcessingContext methodProcessingContext,
- ProgramMethod context,
- Set<Value> affectedValues,
- Set<BasicBlock> blocksToRemove,
- ListIterator<BasicBlock> blocks,
- InstructionListIterator instructions,
- Instruction instruction) {
- if (instruction.isInvokeCustom()) {
- reportInterfaceMethodHandleCallSite(instruction.asInvokeCustom().getCallSite(), context);
- return;
- }
- if (!instruction.isInvokeMethod()) {
- return;
- }
- InvokeMethod invoke = instruction.asInvokeMethod();
- Function<DexMethod, Collection<CfInstruction>> rewriteInvoke =
- (newTarget) -> {
- instructions.replaceCurrentInstruction(
- new InvokeStatic(newTarget, invoke.outValue(), invoke.arguments()));
- return null;
- };
- if (instruction.isInvokeDirect()) {
- rewriteInvokeDirect(invoke.getInvokedMethod(), context, rewriteInvoke);
- } else if (instruction.isInvokeVirtual() || instruction.isInvokeInterface()) {
- rewriteInvokeInterfaceOrInvokeVirtual(
- invoke.getInvokedMethod(), invoke.getInterfaceBit(), rewriteInvoke, null);
- } else {
- Function<SingleResolutionResult, Collection<CfInstruction>> rewriteToThrow =
- (resolutionResult) ->
- rewriteInvokeToThrowIR(
- invoke,
- resolutionResult,
- code,
- blocks,
- instructions,
- affectedValues,
- blocksToRemove,
- methodProcessor,
- methodProcessingContext);
- if (instruction.isInvokeStatic()) {
- // TODO(b/192439456): Make a test to prove resolution is needed here and fix it.
- rewriteInvokeStatic(
- invoke.getInvokedMethod(),
- invoke.getInterfaceBit(),
- methodProcessingContext,
- context,
- synthesizedMethods::add,
- rewriteInvoke,
- rewriteToThrow,
- synthesizedMethods::add);
- } else {
- assert instruction.isInvokeSuper();
- rewriteInvokeSuper(invoke.getInvokedMethod(), context, rewriteInvoke, rewriteToThrow);
- }
- }
- }
-
private boolean isSyntheticMethodThatShouldNotBeDoubleProcessed(ProgramMethod method) {
return appView.getSyntheticItems().isSyntheticMethodThatShouldNotBeDoubleProcessed(method);
}
@@ -693,7 +590,8 @@
private Collection<CfInstruction> rewriteInvokeDirect(
DexMethod invokedMethod,
ProgramMethod context,
- Function<DexMethod, Collection<CfInstruction>> rewriteInvoke) {
+ Function<DexMethod, Collection<CfInstruction>> rewriteInvoke,
+ InterfaceMethodDesugaringEventConsumer eventConsumer) {
if (factory.isConstructor(invokedMethod)) {
return null;
}
@@ -723,19 +621,24 @@
// 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.
assert invokeNeedsRewriting(invokedMethod, DIRECT);
+ DexClassAndMethod companionMethodDefinition = null;
DexMethod companionMethod;
if (directTarget.getDefinition().isPrivateMethod()) {
- companionMethod =
- directTarget.isProgramMethod()
- ? helper
- .ensurePrivateAsMethodOfProgramCompanionClassStub(
- directTarget.asProgramMethod())
- .getReference()
- // TODO(b/183998768): Why does this not create a stub on the class path?
- : helper.privateAsMethodOfCompanionClass(directTarget);
+ if (directTarget.isProgramMethod()) {
+ companionMethodDefinition =
+ helper.ensurePrivateAsMethodOfProgramCompanionClassStub(
+ directTarget.asProgramMethod());
+ companionMethod = companionMethodDefinition.getReference();
+ } else {
+ // TODO(b/183998768): Why does this not create a stub on the class path?
+ companionMethod = helper.privateAsMethodOfCompanionClass(directTarget);
+ }
} else {
- companionMethod =
- helper.ensureDefaultAsMethodOfCompanionClassStub(directTarget).getReference();
+ companionMethodDefinition = helper.ensureDefaultAsMethodOfCompanionClassStub(directTarget);
+ companionMethod = companionMethodDefinition.getReference();
+ }
+ if (companionMethodDefinition != null) {
+ acceptCompanionMethod(directTarget, companionMethodDefinition, eventConsumer);
}
return rewriteInvoke.apply(companionMethod);
} else {
@@ -745,8 +648,10 @@
if (virtualTarget != null) {
// This is a invoke-direct call to a virtual method.
assert invokeNeedsRewriting(invokedMethod, DIRECT);
- return rewriteInvoke.apply(
- helper.ensureDefaultAsMethodOfCompanionClassStub(virtualTarget).getReference());
+ DexClassAndMethod companionMethod =
+ helper.ensureDefaultAsMethodOfCompanionClassStub(virtualTarget);
+ acceptCompanionMethod(virtualTarget, companionMethod, eventConsumer);
+ return rewriteInvoke.apply(companionMethod.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.
@@ -756,6 +661,16 @@
return null;
}
+ private void acceptCompanionMethod(
+ DexClassAndMethod method,
+ DexClassAndMethod companion,
+ InterfaceMethodDesugaringEventConsumer eventConsumer) {
+ assert method.isProgramMethod() == companion.isProgramMethod();
+ if (method.isProgramMethod()) {
+ eventConsumer.acceptCompanionMethod(method.asProgramMethod(), companion.asProgramMethod());
+ }
+ }
+
private Collection<CfInstruction> rewriteInvokeStatic(
DexMethod invokedMethod,
boolean interfaceBit,
@@ -764,7 +679,7 @@
Consumer<ProgramMethod> staticOutliningMethodConsumer,
Function<DexMethod, Collection<CfInstruction>> rewriteInvoke,
Function<SingleResolutionResult, Collection<CfInstruction>> rewriteToThrow,
- Consumer<ProgramMethod> companionClinitConsumer) {
+ CfInstructionDesugaringEventConsumer eventConsumer) {
if (appView.getSyntheticItems().isPendingSynthetic(invokedMethod.holder)) {
// We did not create this code yet, but it will not require rewriting.
return null;
@@ -858,9 +773,9 @@
assert resolutionResult != null;
assert resolutionResult.getResolvedMethod().isStatic();
assert invokeNeedsRewriting(invokedMethod, STATIC);
+ DexClassAndMethod method = resolutionResult.getResolutionPair();
DexClassAndMethod companionMethod =
- helper.ensureStaticAsMethodOfCompanionClassStub(
- resolutionResult.getResolutionPair(), companionClinitConsumer);
+ helper.ensureStaticAsMethodOfCompanionClassStub(method, eventConsumer);
return rewriteInvoke.apply(companionMethod.getReference());
}
@@ -868,7 +783,8 @@
DexMethod invokedMethod,
ProgramMethod context,
Function<DexMethod, Collection<CfInstruction>> rewriteInvoke,
- Function<SingleResolutionResult, Collection<CfInstruction>> rewriteToThrow) {
+ Function<SingleResolutionResult, Collection<CfInstruction>> rewriteToThrow,
+ InterfaceMethodDesugaringEventConsumer eventConsumer) {
DexClass clazz = appView.definitionFor(invokedMethod.holder, context);
if (clazz == null) {
// NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
@@ -901,10 +817,27 @@
// TODO(b/145775365): This should throw IAE.
return rewriteToThrow.apply(null);
}
- return rewriteInvoke.apply(
- helper.privateAsMethodOfCompanionClass(resolutionResult.getResolutionPair()));
+ DexClassAndMethod method = resolutionResult.getResolutionPair();
+ DexMethod companionMethod;
+ if (method.isProgramMethod()) {
+ ProgramMethod companionMethodDefinition =
+ helper.ensurePrivateAsMethodOfProgramCompanionClassStub(method.asProgramMethod());
+ companionMethod = companionMethodDefinition.getReference();
+ eventConsumer.acceptCompanionMethod(method.asProgramMethod(), companionMethodDefinition);
+ } else {
+ companionMethod = helper.privateAsMethodOfCompanionClass(method);
+ }
+ return rewriteInvoke.apply(companionMethod);
} else {
+ DexClassAndMethod method = resolutionResult.getResolutionPair();
+ // TODO(b/183998768): Why do this amend routine. We have done resolution, so would that
+ // not be the correct target!? I think this is just legacy from before resolution was
+ // implemented in full.
DexMethod amendedMethod = amendDefaultMethod(context.getHolder(), invokedMethod);
+ assert method.getReference() == amendedMethod;
+ DexClassAndMethod companionMethod =
+ helper.ensureDefaultAsMethodOfCompanionClassStub(method);
+ acceptCompanionMethod(method, companionMethod, eventConsumer);
return rewriteInvoke.apply(
InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass(
amendedMethod, appView.dexItemFactory()));
@@ -920,8 +853,10 @@
DexClass holder = target.getHolder();
if (holder.isLibraryClass() && holder.isInterface()) {
assert invokeNeedsRewriting(invokedMethod, SUPER);
- return rewriteInvoke.apply(
- helper.ensureDefaultAsMethodOfCompanionClassStub(target).getReference());
+ DexClassAndMethod companionTarget =
+ helper.ensureDefaultAsMethodOfCompanionClassStub(target);
+ acceptCompanionMethod(target, companionTarget, eventConsumer);
+ return rewriteInvoke.apply(companionTarget.getReference());
}
}
}
@@ -1121,26 +1056,15 @@
return singleCandidate != null ? singleCandidate : method;
}
- public void finalizeInterfaceMethodRewritingThroughIR(
- IRConverter converter, ExecutorService executorService) throws ExecutionException {
- SortedProgramMethodSet sortedSynthesizedMethods = SortedProgramMethodSet.create();
- sortedSynthesizedMethods.addAll(synthesizedMethods);
- converter.processMethodsConcurrently(sortedSynthesizedMethods, executorService);
-
- // Cached data is not needed any more.
- this.cache.clear();
- this.synthesizedMethods.clear();
+ public InterfaceMethodProcessorFacade getPostProcessingDesugaringD8(Flavor flavour) {
+ return new InterfaceMethodProcessorFacade(appView, flavour, m -> true);
}
- public void runInterfaceDesugaringProcessorsForR8(
- IRConverter converter, Flavor flavour, ExecutorService executorService)
- throws ExecutionException {
- getPostProcessingDesugaring(flavour)
- .runInterfaceDesugaringProcessorsForR8(converter, executorService);
- }
-
- public InterfaceMethodProcessorFacade getPostProcessingDesugaring(Flavor flavour) {
- return new InterfaceMethodProcessorFacade(appView, flavour);
+ public InterfaceMethodProcessorFacade getPostProcessingDesugaringR8(
+ Flavor flavour,
+ Predicate<ProgramMethod> isLiveMethod,
+ InterfaceProcessor interfaceProcessor) {
+ return new InterfaceMethodProcessorFacade(appView, flavour, isLiveMethod, interfaceProcessor);
}
private Origin getMethodOrigin(DexMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java
index 8fc50d1..e16f254 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java
@@ -4,12 +4,18 @@
package com.android.tools.r8.ir.desugar.itf;
+import com.android.tools.r8.graph.DexClasspathClass;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramMethod;
-public interface InterfaceProcessingDesugaringEventConsumer {
+// TODO(b/183998768): Consider forcing the processing of interface methods in D8 akin to R8.
+// That would avoid the need to reiterate the interface methods to collect info and this
+// could avoid the "base" methods.
+public interface InterfaceProcessingDesugaringEventConsumer
+ extends InterfaceMethodDesugaringBaseEventConsumer {
void acceptForwardingMethod(ProgramMethod method);
- // TODO(b/183998768): Remove this once interface desugaring is moved to the R8 enqueuer.
- void acceptCompanionClassClinit(ProgramMethod method);
+ void acceptEmulatedInterfaceMarkerInterface(
+ DexProgramClass clazz, DexClasspathClass newInterface);
}
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 ac12741..dd4a7a9 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
@@ -9,7 +9,6 @@
import com.android.tools.r8.code.Instruction;
import com.android.tools.r8.code.InvokeSuper;
import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexClass;
@@ -33,7 +32,6 @@
import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
-import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap;
import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -44,6 +42,7 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
// Default and static method interface desugaring processor for interfaces.
@@ -59,14 +58,22 @@
private final Map<DexProgramClass, PostProcessingInterfaceInfo> postProcessingInterfaceInfos =
new ConcurrentHashMap<>();
- InterfaceProcessor(AppView<?> appView) {
+ public InterfaceProcessor(AppView<?> appView) {
this.appView = appView;
helper = new InterfaceDesugaringSyntheticHelper(appView);
}
+ public InterfaceDesugaringSyntheticHelper getHelper() {
+ return helper;
+ }
+
@Override
public void process(
DexProgramClass iface, InterfaceProcessingDesugaringEventConsumer eventConsumer) {
+ if (appView.enableWholeProgramOptimizations()) {
+ // R8 populates all info as part of enqueuing.
+ return;
+ }
if (!iface.isInterface()) {
return;
}
@@ -75,9 +82,9 @@
}
private void analyzeBridges(DexProgramClass iface) {
+ assert !appView.enableWholeProgramOptimizations();
for (ProgramMethod method : iface.virtualProgramMethods()) {
- DexEncodedMethod virtual = method.getDefinition();
- if (!interfaceMethodRemovalChangesApi(virtual, iface)) {
+ if (!interfaceMethodRemovalChangesApi(method, iface)) {
getPostProcessingInterfaceInfo(iface).setHasBridgesToRemove();
return;
}
@@ -86,9 +93,6 @@
private void ensureCompanionClassMethods(
DexProgramClass iface, InterfaceProcessingDesugaringEventConsumer eventConsumer) {
- // TODO(b/183998768): Once fixed, the methods should be added for processing.
- // D8 and R8 don't need to optimize the methods since they are just moved from interfaces and
- // don't need to be re-processed.
processVirtualInterfaceMethods(iface);
processDirectInterfaceMethods(iface, eventConsumer);
}
@@ -121,96 +125,76 @@
}
private void processVirtualInterfaceMethods(DexProgramClass iface) {
+ assert !appView.enableWholeProgramOptimizations();
for (ProgramMethod method : iface.virtualProgramMethods()) {
DexEncodedMethod virtual = method.getDefinition();
if (helper.isCompatibleDefaultMethod(virtual)) {
- if (!canMoveToCompanionClass(virtual)) {
- throw new CompilationError(
- "One or more instruction is preventing default interface "
- + "method from being desugared: "
- + method.toSourceString(),
- iface.origin);
- }
- Code code = virtual.getCode();
- if (code == null) {
- throw new CompilationError(
- "Code is missing for default " + "interface method: " + method.toSourceString(),
- iface.origin);
- }
// Create a new method in a companion class to represent default method implementation.
ProgramMethod companion = helper.ensureDefaultAsMethodOfProgramCompanionClassStub(method);
- DexEncodedMethod.setDebugInfoWithFakeThisParameter(
- code, companion.getReference().getArity(), appView);
finalizeMoveToCompanionMethod(method, companion);
- getPostProcessingInterfaceInfo(iface)
- .mapDefaultMethodToCompanionMethod(virtual, companion.getDefinition());
}
}
}
private void processDirectInterfaceMethods(
DexProgramClass iface, InterfaceProcessingDesugaringEventConsumer eventConsumer) {
+ assert !appView.enableWholeProgramOptimizations();
for (ProgramMethod method : iface.directProgramMethods()) {
DexEncodedMethod definition = method.getDefinition();
- if (definition.isClassInitializer()) {
- continue;
+ if (!definition.isClassInitializer()) {
+ getPostProcessingInterfaceInfo(iface).setHasNonClinitDirectMethods();
+ ProgramMethod companion =
+ helper.ensureMethodOfProgramCompanionClassStub(method, eventConsumer);
+ finalizeMoveToCompanionMethod(method, companion);
}
- if (definition.isInstanceInitializer()) {
- assert false
- : "Unexpected interface instance initializer: "
- + method.getReference().toSourceString();
- continue;
- }
-
- getPostProcessingInterfaceInfo(iface).setHasNonClinitDirectMethods();
-
- ProgramMethod companion;
- if (isStaticMethod(definition)) {
- assert definition.isPrivate() || definition.isPublic()
- : "Static interface method "
- + method.toSourceString()
- + " is expected to "
- + "either be public or private in "
- + iface.origin;
- companion =
- helper.ensureStaticAsMethodOfProgramCompanionClassStub(
- method, eventConsumer::acceptCompanionClassClinit);
- } else {
- assert definition.isPrivate();
- Code code = definition.getCode();
- if (code == null) {
- throw new CompilationError(
- "Code is missing for private instance "
- + "interface method: "
- + method.getReference().toSourceString(),
- iface.origin);
- }
- companion = helper.ensurePrivateAsMethodOfProgramCompanionClassStub(method);
- DexEncodedMethod.setDebugInfoWithFakeThisParameter(
- code, companion.getReference().getArity(), appView);
- }
-
- finalizeMoveToCompanionMethod(method, companion);
- getPostProcessingInterfaceInfo(iface)
- .moveMethod(method.getReference(), companion.getReference());
}
}
- 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());
+ public void finalizeMoveToCompanionMethod(ProgramMethod method, ProgramMethod companion) {
+ assert InvalidCode.isInvalidCode(companion.getDefinition().getCode());
+ if (method.getDefinition().getCode() == null) {
+ throw new CompilationError(
+ "Code is missing for private instance "
+ + "interface method: "
+ + method.getReference().toSourceString(),
+ method.getOrigin());
+ }
+ if (!canMoveToCompanionClass(method)) {
+ throw new CompilationError(
+ "One or more instruction is preventing default interface "
+ + "method from being desugared: "
+ + method.toSourceString(),
+ method.getOrigin());
+ }
DexProgramClass iface = method.getHolder();
DexEncodedMethod definition = method.getDefinition();
+ assert !definition.isInitializer();
+ assert !definition.isStatic() || definition.isPrivate() || definition.isPublic()
+ : "Static interface method "
+ + method.toSourceString()
+ + " is expected to "
+ + "either be public or private in "
+ + method.getOrigin();
+
+ if (definition.isStatic() || definition.isPrivate()) {
+ getPostProcessingInterfaceInfo(iface).setHasNonClinitDirectMethods();
+ getPostProcessingInterfaceInfo(iface)
+ .moveMethod(method.getReference(), companion.getReference());
+ } else {
+ assert helper.isCompatibleDefaultMethod(definition);
+ getPostProcessingInterfaceInfo(iface)
+ .mapDefaultMethodToCompanionMethod(method.getDefinition(), companion.getDefinition());
+ }
if (definition.hasClassFileVersion()) {
companion.getDefinition().downgradeClassFileVersion(definition.getClassFileVersion());
}
- companion
- .getDefinition()
- .setCode(
- definition.getCode().getCodeAsInlining(companion.getReference(), method.getReference()),
- appView);
+ Code code =
+ definition.getCode().getCodeAsInlining(companion.getReference(), method.getReference());
+ if (!definition.isStatic()) {
+ DexEncodedMethod.setDebugInfoWithFakeThisParameter(
+ code, companion.getReference().getArity(), appView);
+ }
+ companion.getDefinition().setCode(code, appView);
definition.setCode(InvalidCode.getInstance(), appView);
}
@@ -224,8 +208,8 @@
}
}
- private boolean canMoveToCompanionClass(DexEncodedMethod method) {
- Code code = method.getCode();
+ private static boolean canMoveToCompanionClass(ProgramMethod method) {
+ Code code = method.getDefinition().getCode();
assert code != null;
if (code.isDexCode()) {
for (Instruction insn : code.asDexCode().instructions) {
@@ -254,13 +238,9 @@
// implementation to the companion class of [iface]. This is always the case for non-bridge
// methods. Bridge methods that does not override an implementation in a super-interface must
// also be kept (such a situation can happen if the vertical class merger merges two interfaces).
- private boolean interfaceMethodRemovalChangesApi(DexEncodedMethod method, DexClass iface) {
- if (appView.enableWholeProgramOptimizations()) {
- if (appView.appInfo().withLiveness().isPinned(method.getReference())) {
- return true;
- }
- }
- if (method.accessFlags.isBridge()) {
+ private boolean interfaceMethodRemovalChangesApi(ProgramMethod method, DexClass iface) {
+ assert !appView.enableWholeProgramOptimizations();
+ if (method.getAccessFlags().isBridge()) {
if (appView.options().cfToCfDesugar) {
// TODO(b/187176895): Find the compilation causing this to not be removed.
return false;
@@ -292,27 +272,22 @@
}
}
- private boolean isStaticMethod(DexEncodedMethod method) {
- if (method.accessFlags.isNative()) {
- throw new Unimplemented("Native interface methods are not yet supported.");
- }
- return method.accessFlags.isStatic()
- && !appView.dexItemFactory().isClassConstructor(method.getReference());
- }
-
private InterfaceProcessorNestedGraphLens postProcessInterfaces() {
InterfaceProcessorNestedGraphLens.Builder graphLensBuilder =
InterfaceProcessorNestedGraphLens.builder();
postProcessingInterfaceInfos.forEach(
(iface, info) -> {
- if (info.hasNonClinitDirectMethods()) {
+ if (info.hasNonClinitDirectMethods() || appView.enableWholeProgramOptimizations()) {
clearDirectMethods(iface);
}
if (info.hasDefaultMethodsToImplementationMap()) {
info.getDefaultMethodsToImplementation()
.forEach(
(defaultMethod, companionMethod) -> {
- defaultMethod.setDefaultInterfaceMethodImplementation(companionMethod);
+ assert InvalidCode.isInvalidCode(defaultMethod.getCode());
+ assert !InvalidCode.isInvalidCode(companionMethod.getCode());
+ defaultMethod.accessFlags.setAbstract();
+ defaultMethod.removeCode();
graphLensBuilder.recordCodeMovedToCompanionClass(
defaultMethod.getReference(), companionMethod.getReference());
});
@@ -321,6 +296,8 @@
info.getMethodsToMove().forEach(graphLensBuilder::move);
}
if (info.hasBridgesToRemove()) {
+ // D8 can remove bridges at this point.
+ assert !appView.enableWholeProgramOptimizations();
removeBridges(iface);
}
});
@@ -328,12 +305,12 @@
}
private void removeBridges(DexProgramClass iface) {
+ assert !appView.enableWholeProgramOptimizations();
List<DexEncodedMethod> newVirtualMethods = new ArrayList<>();
for (ProgramMethod method : iface.virtualProgramMethods()) {
- DexEncodedMethod virtual = method.getDefinition();
// Remove bridge methods.
- if (interfaceMethodRemovalChangesApi(virtual, iface)) {
- newVirtualMethods.add(virtual);
+ if (interfaceMethodRemovalChangesApi(method, iface)) {
+ newVirtualMethods.add(method.getDefinition());
}
}
@@ -350,14 +327,11 @@
@Override
public void finalizeProcessing(InterfaceProcessingDesugaringEventConsumer eventConsumer) {
+ // TODO(b/196337368): Simplify this fix-up to be specific for the move of companion methods
+ // rather than be based on a graph lens.
InterfaceProcessorNestedGraphLens graphLens = postProcessInterfaces();
if (graphLens != null) {
- if (appView.enableWholeProgramOptimizations()) {
- appView.setGraphLens(graphLens);
- }
new InterfaceMethodRewriterFixup(appView, graphLens).run();
-
- graphLens.moveToPending();
}
}
@@ -366,6 +340,15 @@
iface, ignored -> new PostProcessingInterfaceInfo());
}
+ public void forEachMethodToMove(BiConsumer<DexMethod, DexMethod> fn) {
+ postProcessingInterfaceInfos.forEach(
+ (iface, info) -> {
+ if (info.methodsToMove != null) {
+ info.methodsToMove.forEach(fn);
+ }
+ });
+ }
+
static class PostProcessingInterfaceInfo {
private Map<DexEncodedMethod, DexEncodedMethod> defaultMethodsToImplementation;
private Map<DexMethod, DexMethod> methodsToMove;
@@ -422,14 +405,11 @@
// Specific lens which remaps invocation types to static since all rewrites performed here
// are to static companion methods.
- // TODO(b/167345026): Remove the use of this lens.
+ // TODO(b/196337368): Replace this by a desugaring lens shared for D8 and R8.
public static class InterfaceProcessorNestedGraphLens extends NestedGraphLens {
- private BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod> extraNewMethodSignatures;
- private BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod>
- pendingNewMethodSignatures;
- private BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod>
- pendingExtraNewMethodSignatures;
+ private final BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod>
+ extraNewMethodSignatures;
public InterfaceProcessorNestedGraphLens(
AppView<?> appView,
@@ -441,55 +421,12 @@
this.extraNewMethodSignatures = extraNewMethodSignatures;
}
- public void moveToPending() {
- // These are "pending" and installed in the "toggled" state only.
- pendingNewMethodSignatures = newMethodSignatures;
- pendingExtraNewMethodSignatures = extraNewMethodSignatures;
- // The interface methods do not contribute to renaming lens info anymore, so they are cleared.
- newMethodSignatures = new EmptyBidirectionalOneToOneMap<>();
- this.extraNewMethodSignatures = new EmptyBidirectionalOneToOneMap<>();
- }
-
- public static InterfaceProcessorNestedGraphLens find(GraphLens lens) {
- if (lens.isInterfaceProcessorLens()) {
- return lens.asInterfaceProcessorLens();
- }
- if (lens.isIdentityLens()) {
- return null;
- }
- if (lens.isNonIdentityLens()) {
- return find(lens.asNonIdentityLens().getPrevious());
- }
- assert false;
- return null;
- }
-
- public void enableMapping() {
- this.newMethodSignatures = pendingExtraNewMethodSignatures;
- this.extraNewMethodSignatures = pendingNewMethodSignatures;
- }
-
- public void disableMapping() {
- this.newMethodSignatures = new EmptyBidirectionalOneToOneMap<>();
- this.extraNewMethodSignatures = new EmptyBidirectionalOneToOneMap<>();
- }
-
public BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod>
getExtraNewMethodSignatures() {
return extraNewMethodSignatures;
}
@Override
- public boolean isInterfaceProcessorLens() {
- return true;
- }
-
- @Override
- public InterfaceProcessorNestedGraphLens asInterfaceProcessorLens() {
- return this;
- }
-
- @Override
public boolean isLegitimateToHaveEmptyMappings() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
index 39a6ae1..7c26d30 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
@@ -413,7 +413,6 @@
@Override
public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
- assert !instruction.isInitClass();
if (instruction.isInvokeDynamic()) {
return needsDesugaring(instruction.asInvokeDynamic(), context);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index c07ad6d..dba8278 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -54,7 +54,6 @@
import com.android.tools.r8.ir.code.ValueTypeConstraint;
import com.android.tools.r8.ir.conversion.IRBuilder;
import com.android.tools.r8.ir.conversion.SourceCode;
-import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor.InterfaceProcessorNestedGraphLens;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.origin.Origin;
@@ -1315,29 +1314,12 @@
public ProgramMethodSet selectMethodsForOutlining() {
ProgramMethodSet methodsSelectedForOutlining = ProgramMethodSet.create();
assert outlineSites.isEmpty();
-
- // TODO(b/167345026): This is needed to ensure that default interface methods are mapped to
- // the corresponding companion methods that contain the code objects. This should be removed
- // once default interface methods are desugared prior to the first optimization pass.
- InterfaceProcessorNestedGraphLens interfaceProcessorLens =
- InterfaceProcessorNestedGraphLens.find(appView.graphLens());
- if (interfaceProcessorLens != null) {
- interfaceProcessorLens.enableMapping();
- }
-
for (LongLivedProgramMethodMultisetBuilder outlineMethods : candidateMethodLists) {
if (outlineMethods.size() >= appView.options().outline.threshold) {
ProgramMethodMultiset multiset = outlineMethods.build(appView);
multiset.forEachEntry((method, ignore) -> methodsSelectedForOutlining.add(method));
}
}
-
- // TODO(b/167345026): Remove once default interface methods are desugared prior to the first
- // optimization pass.
- if (interfaceProcessorLens != null) {
- interfaceProcessorLens.disableMapping();
- }
-
candidateMethodLists.clear();
return methodsSelectedForOutlining;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 41eb11b..483d005 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.graph.FieldAccessInfoImpl.MISSING_FIELD_ACCESS_INFO;
import static com.android.tools.r8.ir.desugar.LambdaDescriptor.isLambdaMetafactoryMethod;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor.ExcludeDexResources;
import static com.android.tools.r8.naming.IdentifierNameStringUtils.identifyIdentifier;
import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument;
@@ -59,6 +60,7 @@
import com.android.tools.r8.graph.FieldResolutionResult;
import com.android.tools.r8.graph.GenericSignatureEnqueuerAnalysis;
import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.graph.InvalidCode;
import com.android.tools.r8.graph.LookupLambdaTarget;
import com.android.tools.r8.graph.LookupTarget;
import com.android.tools.r8.graph.MethodAccessInfoCollection;
@@ -99,6 +101,8 @@
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.ir.desugar.ProgramAdditions;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
+import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
+import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor;
import com.android.tools.r8.kotlin.KotlinMetadataEnqueuerExtension;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringLookupResult;
@@ -120,6 +124,7 @@
import com.android.tools.r8.shaking.ScopedDexMethodSet.AddMethodIfMoreVisibleResult;
import com.android.tools.r8.synthesis.SyntheticItems.SynthesizingContextOracle;
import com.android.tools.r8.utils.Action;
+import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IteratorUtils;
import com.android.tools.r8.utils.OptionalBool;
@@ -131,10 +136,10 @@
import com.android.tools.r8.utils.Visibility;
import com.android.tools.r8.utils.WorkList;
import com.android.tools.r8.utils.collections.ProgramFieldSet;
+import com.android.tools.r8.utils.collections.ProgramMethodMap;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;
@@ -423,7 +428,20 @@
private final GraphReporter graphReporter;
private final CfInstructionDesugaringCollection desugaring;
- private final ProgramMethodSet pendingDesugaring = ProgramMethodSet.create();
+ private final ProgramMethodSet pendingCodeDesugaring = ProgramMethodSet.create();
+
+ // Collections for tracing progress on interface method desugaring.
+
+ // The pending method move set is all the methods that need to be moved to companions.
+ // They may or may not need desugaring.
+ private final ProgramMethodSet pendingMethodMove = ProgramMethodSet.create();
+
+ // The inverse map records references to companion methods that may now be active but yet to
+ // be moved.
+ private final ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse =
+ ProgramMethodMap.createConcurrent();
+
+ private final InterfaceProcessor interfaceProcessor;
Enqueuer(
AppView<? extends AppInfoWithClassHierarchy> appView,
@@ -466,10 +484,13 @@
failedFieldResolutionTargets = SetUtils.newIdentityHashSet(0);
liveMethods = new LiveMethodsSet(graphReporter::registerMethod);
liveFields = new LiveFieldsSet(graphReporter::registerField);
- desugaring =
- mode.isInitialTreeShaking()
- ? CfInstructionDesugaringCollection.create(appView)
- : CfInstructionDesugaringCollection.empty();
+ if (mode.isInitialTreeShaking()) {
+ desugaring = CfInstructionDesugaringCollection.create(appView);
+ interfaceProcessor = new InterfaceProcessor(appView);
+ } else {
+ desugaring = CfInstructionDesugaringCollection.empty();
+ interfaceProcessor = null;
+ }
objectAllocationInfoCollection =
ObjectAllocationInfoCollectionImpl.builder(mode.isInitialTreeShaking(), graphReporter);
@@ -3080,6 +3101,7 @@
public EnqueuerResult traceApplication(
RootSet rootSet, ExecutorService executorService, Timing timing) throws ExecutionException {
this.rootSet = rootSet;
+ rootSet.pendingMethodMoveInverse.forEach(pendingMethodMoveInverse::put);
// Translate the result of root-set computation into enqueuer actions.
if (mode.isTreeShaking()
&& appView.options().hasProguardConfiguration()
@@ -3355,7 +3377,7 @@
public void addLiveClasspathClass(DexClasspathClass clazz) {
DexClasspathClass old = syntheticClasspathClasses.put(clazz.type, clazz);
- assert old == null;
+ assert old == null || old == clazz;
}
public void addLiveMethods(Iterable<ProgramMethod> methods) {
@@ -3364,8 +3386,12 @@
public void addLiveMethod(ProgramMethod method) {
DexMethod signature = method.getDefinition().getReference();
- assert !liveMethods.containsKey(signature);
- liveMethods.put(signature, method);
+ ProgramMethod old = liveMethods.put(signature, method);
+ assert old == null;
+ }
+
+ public void addMethodWithDesugaredCodeForTracing(ProgramMethod method) {
+ desugaredMethods.add(method);
}
public void injectInterface(DexProgramClass clazz, DexClass newInterface) {
@@ -3436,31 +3462,94 @@
additions.enqueueWorkItems(this);
}
+ private boolean mustMoveToInterfaceCompanionMethod(ProgramMethod method) {
+ return method.getHolder().isInterface()
+ && method.getDefinition().isNonAbstractNonNativeMethod()
+ && !method.getDefinition().isInitializer();
+ }
+
+ private boolean addToPendingDesugaring(ProgramMethod method) {
+ if (options.isInterfaceMethodDesugaringEnabled()) {
+ if (mustMoveToInterfaceCompanionMethod(method)) {
+ pendingMethodMove.add(method);
+ return true;
+ }
+ ProgramMethod nonMovedMethod = pendingMethodMoveInverse.get(method);
+ if (nonMovedMethod != null) {
+ // Any non-moved code must be a proper pending item.
+ assert InvalidCode.isInvalidCode(method.getDefinition().getCode());
+ assert !InvalidCode.isInvalidCode(nonMovedMethod.getDefinition().getCode());
+ pendingMethodMove.add(nonMovedMethod);
+ return true;
+ }
+ }
+ if (desugaring.needsDesugaring(method)) {
+ pendingCodeDesugaring.add(method);
+ return true;
+ }
+ return false;
+ }
+
private void desugar(SyntheticAdditions additions) throws ExecutionException {
- if (pendingDesugaring.isEmpty()) {
+ if (pendingCodeDesugaring.isEmpty() && pendingMethodMove.isEmpty()) {
return;
}
- // Prepare desugaring by collecting all the synthetic methods required on program classes.
- ProgramAdditions programAdditions = new ProgramAdditions();
- ThreadUtils.processItems(
- pendingDesugaring, method -> desugaring.prepare(method, programAdditions), executorService);
- programAdditions.apply(executorService);
+ // All non-moving methods are ready for tracing post desugar.
+ pendingCodeDesugaring.forEach(additions::addMethodWithDesugaredCodeForTracing);
+ // Then amend the desugar set with the move methods that need desugaring.
+ for (ProgramMethod method : pendingMethodMove) {
+ if (desugaring.needsDesugaring(method)) {
+ pendingCodeDesugaring.add(method);
+ }
+ }
- R8CfInstructionDesugaringEventConsumer desugaringEventConsumer =
+ R8CfInstructionDesugaringEventConsumer eventConsumer =
CfInstructionDesugaringEventConsumer.createForR8(
appView,
this::recordLambdaSynthesizingContext,
this::recordTwrCloseResourceMethodSynthesizingContext,
- additions);
+ additions,
+ (method, companion) -> {
+ if (!isMethodLive(method)) {
+ // Record the original placement of the companion method such that we can desugar
+ // and transfer the code if and when the companion method becomes live.
+ pendingMethodMoveInverse.put(companion, method);
+ }
+ });
+
+ // Prepare desugaring by collecting all the synthetic methods required on program classes.
+ ProgramAdditions programAdditions = new ProgramAdditions();
ThreadUtils.processItems(
- pendingDesugaring,
- method ->
- desugaring.desugar(method, additions.getMethodContext(method), desugaringEventConsumer),
+ pendingCodeDesugaring,
+ method -> desugaring.prepare(method, programAdditions),
executorService);
- desugaringEventConsumer.finalizeDesugaring();
- Iterables.addAll(additions.desugaredMethods, pendingDesugaring);
- pendingDesugaring.clear();
+ programAdditions.apply(executorService);
+
+ // Then do the actual desugaring.
+ ThreadUtils.processItems(
+ pendingCodeDesugaring,
+ method -> desugaring.desugar(method, additions.getMethodContext(method), eventConsumer),
+ executorService);
+
+ // Move the pending methods and mark them live and ready for tracing.
+ for (ProgramMethod method : pendingMethodMove) {
+ ProgramMethod companion =
+ interfaceProcessor
+ .getHelper()
+ .ensureMethodOfProgramCompanionClassStub(method, eventConsumer);
+ interfaceProcessor.finalizeMoveToCompanionMethod(method, companion);
+ pendingMethodMoveInverse.remove(companion);
+ if (!isMethodLive(companion)) {
+ additions.addLiveMethod(companion);
+ }
+ additions.addMethodWithDesugaredCodeForTracing(companion);
+ }
+
+ eventConsumer.finalizeDesugaring();
+
+ pendingMethodMove.clear();
+ pendingCodeDesugaring.clear();
}
private void recordLambdaSynthesizingContext(LambdaClass lambdaClass, ProgramMethod context) {
@@ -3481,8 +3570,6 @@
DexProgramClass holder = bridge.getHolder();
DexEncodedMethod method = bridge.getDefinition();
holder.addVirtualMethod(method);
- additions.addLiveMethodWithKeepAction(
- bridge, joiner -> joiner.disallowOptimization().disallowShrinking());
}
syntheticInterfaceMethodBridges.clear();
}
@@ -3593,15 +3680,15 @@
rootSet.mayHaveSideEffects,
rootSet.noSideEffects,
rootSet.assumedValues,
- rootSet.alwaysInline,
- rootSet.forceInline,
- rootSet.neverInline,
- rootSet.neverInlineDueToSingleCaller,
- rootSet.whyAreYouNotInlining,
- rootSet.keepConstantArguments,
- rootSet.keepUnusedArguments,
- rootSet.reprocess,
- rootSet.neverReprocess,
+ amendWithCompanionMethods(rootSet.alwaysInline),
+ amendWithCompanionMethods(rootSet.forceInline),
+ amendWithCompanionMethods(rootSet.neverInline),
+ amendWithCompanionMethods(rootSet.neverInlineDueToSingleCaller),
+ amendWithCompanionMethods(rootSet.whyAreYouNotInlining),
+ amendWithCompanionMethods(rootSet.keepConstantArguments),
+ amendWithCompanionMethods(rootSet.keepUnusedArguments),
+ amendWithCompanionMethods(rootSet.reprocess),
+ amendWithCompanionMethods(rootSet.neverReprocess),
rootSet.alwaysClassInline,
rootSet.neverClassInline,
noClassMerging,
@@ -3620,6 +3707,22 @@
return new EnqueuerResult(appInfoWithLiveness);
}
+ private Set<DexMethod> amendWithCompanionMethods(Set<DexMethod> methods) {
+ if (methods.isEmpty() || interfaceProcessor == null) {
+ return methods;
+ }
+ BooleanBox changed = new BooleanBox(false);
+ ImmutableSet.Builder<DexMethod> builder = ImmutableSet.builder();
+ interfaceProcessor.forEachMethodToMove(
+ (method, companion) -> {
+ if (methods.contains(method)) {
+ changed.set(true);
+ builder.add(companion);
+ }
+ });
+ return changed.isTrue() ? builder.addAll(methods).build() : methods;
+ }
+
private boolean verifyReferences(DexApplication app) {
WorkList<DexClass> worklist = WorkList.newIdentityWorkList();
for (DexProgramClass clazz : liveTypes.getItems()) {
@@ -3762,6 +3865,16 @@
continue;
}
+ for (DelayedRootSetActionItem delayedRootSetActionItem :
+ rootSet.delayedRootSetActionItems) {
+ if (delayedRootSetActionItem.isInterfaceMethodSyntheticBridgeAction()) {
+ identifySyntheticInterfaceMethodBridges(
+ delayedRootSetActionItem.asInterfaceMethodSyntheticBridgeAction());
+ }
+ }
+
+ synthesize();
+
ConsequentRootSet consequentRootSet = computeDelayedInterfaceMethodSyntheticBridges();
addConsequentRootSet(consequentRootSet);
rootSet
@@ -3773,11 +3886,6 @@
continue;
}
- synthesize();
- if (!workList.isEmpty()) {
- continue;
- }
-
// Reached the fixpoint.
break;
}
@@ -3808,9 +3916,14 @@
assert workList.isEmpty();
R8PostProcessingDesugaringEventConsumer eventConsumer =
- CfPostProcessingDesugaringEventConsumer.createForR8(syntheticAdditions);
- CfPostProcessingDesugaringCollection.create(appView, null, desugaring.getRetargetingInfo())
- .postProcessingDesugaring(liveTypes.items, eventConsumer, executorService);
+ CfPostProcessingDesugaringEventConsumer.createForR8(syntheticAdditions, desugaring);
+ InterfaceMethodProcessorFacade interfaceDesugaring =
+ desugaring.getInterfaceMethodPostProcessingDesugaringR8(
+ ExcludeDexResources, liveMethods::contains, interfaceProcessor);
+ CfPostProcessingDesugaringCollection.create(
+ appView, interfaceDesugaring, desugaring.getRetargetingInfo())
+ .postProcessingDesugaring(
+ liveTypes.items, liveMethods::contains, eventConsumer, executorService);
if (syntheticAdditions.isEmpty()) {
return;
@@ -3829,8 +3942,6 @@
EnqueuerAction action = workList.poll();
action.run(this);
}
-
- eventConsumer.finalizeDesugaring();
}
private long getNumberOfLiveItems() {
@@ -3846,6 +3957,7 @@
// enqueuer, similar to Enqueuer#dependentMinimumKeepClassInfo.
rootSet.addConsequentRootSet(consequentRootSet);
includeMinimumKeepInfo(consequentRootSet);
+ consequentRootSet.pendingMethodMoveInverse.forEach(pendingMethodMoveInverse::put);
// Check for compatibility rules indicating that the holder must be implicitly kept.
if (forceProguardCompatibility) {
@@ -3873,11 +3985,10 @@
private final Map<DexMethod, ProgramMethod> syntheticInterfaceMethodBridges =
new LinkedHashMap<>();
- private void handleInterfaceMethodSyntheticBridgeAction(
- InterfaceMethodSyntheticBridgeAction action, RootSetBuilder builder) {
+ private void identifySyntheticInterfaceMethodBridges(
+ InterfaceMethodSyntheticBridgeAction action) {
ProgramMethod methodToKeep = action.getMethodToKeep();
ProgramMethod singleTarget = action.getSingleTarget();
- DexEncodedMethod singleTargetMethod = singleTarget.getDefinition();
if (rootSet.isShrinkingDisallowedUnconditionally(singleTarget, options)) {
return;
}
@@ -3886,20 +3997,19 @@
methodToKeep.getDefinition().getReference())) {
syntheticInterfaceMethodBridges.put(
methodToKeep.getDefinition().getReference(), methodToKeep);
- assert null
- == methodToKeep.getHolder().lookupMethod(methodToKeep.getDefinition().getReference());
- if (singleTargetMethod.isLibraryMethodOverride().isTrue()) {
- methodToKeep.getDefinition().setLibraryMethodOverride(OptionalBool.TRUE);
- }
- DexProgramClass singleTargetHolder = singleTarget.getHolder();
- assert singleTargetHolder.isInterface();
- markVirtualMethodAsReachable(
- singleTargetMethod.getReference(),
- singleTargetHolder.isInterface(),
- singleTarget,
- graphReporter.fakeReportShouldNotBeUsed());
- workList.enqueueMarkMethodLiveAction(
- singleTarget, singleTarget, graphReporter.fakeReportShouldNotBeUsed());
+ }
+ }
+
+ private void handleInterfaceMethodSyntheticBridgeAction(
+ InterfaceMethodSyntheticBridgeAction action, RootSetBuilder builder) {
+ ProgramMethod methodToKeep = action.getMethodToKeep();
+ ProgramMethod singleTarget = action.getSingleTarget();
+ DexEncodedMethod singleTargetMethod = singleTarget.getDefinition();
+ if (rootSet.isShrinkingDisallowedUnconditionally(singleTarget, options)) {
+ return;
+ }
+ if (singleTargetMethod.isLibraryMethodOverride().isTrue()) {
+ methodToKeep.getDefinition().setLibraryMethodOverride(OptionalBool.TRUE);
}
action.getAction().accept(builder);
}
@@ -3922,33 +4032,23 @@
DexEncodedMethod definition = target.getDefinition();
DexProgramClass holder = target.getHolder();
DexMethod reference = target.getReference();
+ markMethodAsTargeted(target, reason);
if (definition.isVirtualMethod()) {
// A virtual method. Mark it as reachable so that subclasses, if instantiated, keep
// their overrides. However, we don't mark it live, as a keep rule might not imply that
// the corresponding class is live.
markVirtualMethodAsReachable(reference, holder.isInterface(), target, reason);
- if (holder.isInterface()) {
- // Reachability for default methods is based on live subtypes in general. For keep rules,
- // we need special handling as we essentially might have live subtypes that are outside of
- // the current compilation unit. Keep either the default-method or its implementation
- // method.
+ // When generating interface bridges the method may be inserted into a live hierarchy.
+ // If so we need to also mark it as live as the reachable check above will not reprocess the
+ // hierarchy.
+ // TODO(b/183998768): The check for isInterface here should be possible to remove now.
+ if (definition.isNonAbstractVirtualMethod()
+ && (objectAllocationInfoCollection.isInstantiatedDirectlyOrHasInstantiatedSubtype(holder)
+ || holder.isInterface())) {
// TODO(b/120959039): Codify the kept-graph expectations for these cases in tests.
- if (definition.isNonAbstractVirtualMethod()) {
- markVirtualMethodAsLive(target, reason);
- } else {
- DexEncodedMethod implementation = definition.getDefaultInterfaceMethodImplementation();
- if (implementation != null) {
- DexProgramClass companion =
- asProgramClassOrNull(appInfo().definitionFor(implementation.getHolderType()));
- markTypeAsLive(companion, graphReporter.reportCompanionClass(holder, companion));
- markVirtualMethodAsLive(
- new ProgramMethod(companion, implementation),
- graphReporter.reportCompanionMethod(definition, implementation));
- }
- }
+ markVirtualMethodAsLive(target, reason);
}
} else {
- markMethodAsTargeted(target, reason);
markDirectStaticOrConstructorMethodAsLive(target, reason);
}
}
@@ -4090,9 +4190,10 @@
}
private void traceNonDesugaredCode(ProgramMethod method) {
- if (getMode().isInitialTreeShaking() && desugaring.needsDesugaring(method)) {
- pendingDesugaring.add(method);
- return;
+ if (getMode().isInitialTreeShaking()) {
+ if (addToPendingDesugaring(method)) {
+ return;
+ }
}
traceCode(method);
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
index cd52fd7..ad3d4d1 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
@@ -501,7 +501,8 @@
@Override
boolean enqueueAssertAction(Action assertion) {
- throw attemptToEnqueue();
+ assertion.execute();
+ return true;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 73f5c94..72a1510 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -42,6 +42,8 @@
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteBuilderShrinker;
+import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper;
+import com.android.tools.r8.ir.desugar.itf.InterfaceMethodDesugaringBaseEventConsumer;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.shaking.AnnotationMatchResult.AnnotationsIgnoredMatchResult;
@@ -58,6 +60,7 @@
import com.android.tools.r8.utils.PredicateSet;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.collections.ProgramMethodMap;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -137,6 +140,10 @@
private final OptimizationFeedbackSimple feedback = OptimizationFeedbackSimple.getInstance();
+ private final InterfaceDesugaringSyntheticHelper interfaceDesugaringSyntheticHelper;
+ private final ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse =
+ ProgramMethodMap.create();
+
private RootSetBuilder(
AppView<? extends AppInfoWithClassHierarchy> appView,
SubtypingInfo subtypingInfo,
@@ -146,6 +153,10 @@
this.application = appView.appInfo().app().asDirect();
this.rules = rules;
this.options = appView.options();
+ interfaceDesugaringSyntheticHelper =
+ options.isInterfaceMethodDesugaringEnabled()
+ ? new InterfaceDesugaringSyntheticHelper(appView)
+ : null;
}
private RootSetBuilder(
@@ -383,7 +394,8 @@
dependentKeepClassCompatRule,
identifierNameStrings,
ifRules,
- Lists.newArrayList(delayedRootSetActionItems));
+ Lists.newArrayList(delayedRootSetActionItems),
+ pendingMethodMoveInverse);
}
private void propagateAssumeRules(DexClass clazz) {
@@ -453,7 +465,8 @@
neverClassInline,
dependentMinimumKeepInfo,
dependentKeepClassCompatRule,
- Lists.newArrayList(delayedRootSetActionItems));
+ Lists.newArrayList(delayedRootSetActionItems),
+ pendingMethodMoveInverse);
}
private static DexProgramClass testAndGetPrecondition(
@@ -1333,25 +1346,6 @@
// Don't keep lambda deserialization methods.
return;
}
- // If desugaring is enabled, private and static interface methods will be moved to a
- // companion class. So we don't need to add them to the root set in the beginning.
- if (options.isInterfaceMethodDesugaringEnabled()
- && method.getDefinition().hasCode()
- && (method.getAccessFlags().isPrivate() || method.getAccessFlags().isStatic())) {
- DexClass holder = appView.definitionFor(method.getHolderType());
- if (holder != null && holder.isInterface()) {
- if (rule.isSpecific()) {
- options.reporter.warning(
- new StringDiagnostic(
- "The rule `"
- + rule
- + "` is ignored because the targeting interface method `"
- + method.getReference().toSourceString()
- + "` will be desugared."));
- }
- return;
- }
- }
}
// The reason for keeping should link to the conditional rule as a whole, if present.
@@ -1388,6 +1382,44 @@
preconditionEvent = UnconditionalKeepInfoEvent.get();
}
+ if (isInterfaceMethodNeedingDesugaring(item)) {
+ ProgramMethod method = item.asMethod();
+ ProgramMethod companion =
+ interfaceDesugaringSyntheticHelper.ensureMethodOfProgramCompanionClassStub(
+ method,
+ new InterfaceMethodDesugaringBaseEventConsumer() {
+ @Override
+ public void acceptCompanionClassClinit(ProgramMethod method) {
+ // No processing of synthesized CC.<clinit>. They will be picked up by tracing.
+ }
+
+ @Override
+ public void acceptCompanionMethod(ProgramMethod method, ProgramMethod companion) {
+ // The move will be included in the pending-inverse map below.
+ }
+ });
+ // Add the method to the inverse map as tracing will now directly target the CC method.
+ pendingMethodMoveInverse.put(companion, method);
+ // Only shrinking and optimization are transferred for interface companion methods.
+ if (appView.options().isOptimizationEnabled() && !modifiers.allowsOptimization) {
+ dependentMinimumKeepInfo
+ .getOrCreateMinimumKeepInfoFor(preconditionEvent, companion.getReference())
+ .disallowOptimization();
+ context.markAsUsed();
+ }
+ if (appView.options().isShrinking() && !modifiers.allowsShrinking) {
+ dependentMinimumKeepInfo
+ .getOrCreateMinimumKeepInfoFor(preconditionEvent, companion.getReference())
+ .addRule(keepRule)
+ .disallowShrinking();
+ context.markAsUsed();
+ }
+ if (!item.asMethod().isDefaultMethod()) {
+ // Static and private methods do not apply to the original item.
+ return;
+ }
+ }
+
if (appView.options().isAccessModificationEnabled() && !modifiers.allowsAccessModification) {
dependentMinimumKeepInfo
.getOrCreateMinimumKeepInfoFor(preconditionEvent, item.getReference())
@@ -1436,6 +1468,14 @@
}
}
+ private boolean isInterfaceMethodNeedingDesugaring(ProgramDefinition item) {
+ return options.isInterfaceMethodDesugaringEnabled()
+ && item.isMethod()
+ && item.asMethod().getHolder().isInterface()
+ && !item.asMethod().getDefinition().isClassInitializer()
+ && item.asMethod().getDefinition().hasCode();
+ }
+
private void reportAssumeNoSideEffectsWarningForJavaLangClassMethod(
DexClassAndMethod method, ProguardAssumeNoSideEffectRule context) {
assert method.getHolderType() == options.dexItemFactory().objectType;
@@ -1486,6 +1526,7 @@
private final DependentMinimumKeepInfoCollection dependentMinimumKeepInfo;
final Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule;
final List<DelayedRootSetActionItem> delayedRootSetActionItems;
+ public final ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse;
RootSetBase(
Set<DexMethod> neverInline,
@@ -1493,13 +1534,15 @@
Set<DexType> neverClassInline,
DependentMinimumKeepInfoCollection dependentMinimumKeepInfo,
Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
- List<DelayedRootSetActionItem> delayedRootSetActionItems) {
+ List<DelayedRootSetActionItem> delayedRootSetActionItems,
+ ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse) {
this.neverInline = neverInline;
this.neverInlineDueToSingleCaller = neverInlineDueToSingleCaller;
this.neverClassInline = neverClassInline;
this.dependentMinimumKeepInfo = dependentMinimumKeepInfo;
this.dependentKeepClassCompatRule = dependentKeepClassCompatRule;
this.delayedRootSetActionItems = delayedRootSetActionItems;
+ this.pendingMethodMoveInverse = pendingMethodMoveInverse;
}
Set<ProguardKeepRuleBase> getDependentKeepClassCompatRule(DexType type) {
@@ -1560,14 +1603,16 @@
Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
Set<DexReference> identifierNameStrings,
Set<ProguardIfRule> ifRules,
- List<DelayedRootSetActionItem> delayedRootSetActionItems) {
+ List<DelayedRootSetActionItem> delayedRootSetActionItems,
+ ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse) {
super(
neverInline,
neverInlineDueToSingleCaller,
neverClassInline,
dependentMinimumKeepInfo,
dependentKeepClassCompatRule,
- delayedRootSetActionItems);
+ delayedRootSetActionItems,
+ pendingMethodMoveInverse);
this.reasonAsked = reasonAsked;
this.checkDiscarded = checkDiscarded;
this.alwaysInline = alwaysInline;
@@ -1880,14 +1925,16 @@
Set<DexType> neverClassInline,
DependentMinimumKeepInfoCollection dependentMinimumKeepInfo,
Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
- List<DelayedRootSetActionItem> delayedRootSetActionItems) {
+ List<DelayedRootSetActionItem> delayedRootSetActionItems,
+ ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse) {
super(
neverInline,
neverInlineDueToSingleCaller,
neverClassInline,
dependentMinimumKeepInfo,
dependentKeepClassCompatRule,
- delayedRootSetActionItems);
+ delayedRootSetActionItems,
+ pendingMethodMoveInverse);
}
static ConsequentRootSetBuilder builder(
@@ -1959,7 +2006,8 @@
emptyMap(),
Collections.emptySet(),
ifRules,
- delayedRootSetActionItems);
+ delayedRootSetActionItems,
+ ProgramMethodMap.empty());
}
public static MainDexRootSetBuilder builder(
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 71ae785..474d1c3 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -731,6 +731,28 @@
return internalEnsureDexProgramClass(kind, fn, onCreationConsumer, outerContext, type, appView);
}
+ public DexClasspathClass ensureFixedClasspathClassFromType(
+ SyntheticKind kind,
+ DexType contextType,
+ AppView<?> appView,
+ Consumer<SyntheticClasspathClassBuilder> fn) {
+ SynthesizingContext outerContext = SynthesizingContext.fromType(contextType);
+ DexType type = SyntheticNaming.createFixedType(kind, outerContext, appView.dexItemFactory());
+ synchronized (contextType) {
+ DexClass clazz = appView.definitionFor(type);
+ if (clazz != null) {
+ assert clazz.isClasspathClass();
+ return clazz.asClasspathClass();
+ }
+ SyntheticClasspathClassBuilder classBuilder =
+ new SyntheticClasspathClassBuilder(type, kind, outerContext, appView.dexItemFactory());
+ fn.accept(classBuilder);
+ DexClasspathClass definition = classBuilder.build();
+ addPendingDefinition(new SyntheticClasspathClassDefinition(kind, outerContext, definition));
+ return definition;
+ }
+ }
+
/** Create a single synthetic method item. */
public ProgramMethod createMethod(
SyntheticKind kind,
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 216fcfc..61d74fd 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -53,7 +53,8 @@
SERVICE_LOADER("ServiceLoad", 18, true),
OUTLINE("Outline", 19, true),
API_CONVERSION("APIConversion", 26, true),
- API_CONVERSION_PARAMETERS("APIConversionParameters", 28, true);
+ API_CONVERSION_PARAMETERS("APIConversionParameters", 28, true),
+ EMULATED_INTERFACE_MARKER_CLASS("", 29, false, true, true);
static {
assert verifyNoOverlappingIds();
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index 089154e..891f1c2 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -25,7 +25,7 @@
* as the singleton list containing {@code v} (i.e., no changes should be made to the given
* element).
*/
- public static <T> List<T> flatMap(
+ public static <T> List<T> flatMapSameType(
List<T> list, Function<T, Collection<T>> fn, List<T> defaultValue) {
List<T> result = null;
for (int i = 0; i < list.size(); i++) {
@@ -48,6 +48,12 @@
return result != null ? result : defaultValue;
}
+ public static <S, T> List<T> flatMap(List<S> list, Function<S, Collection<T>> fn) {
+ List<T> result = new ArrayList<>();
+ list.forEach(element -> result.addAll(fn.apply(element)));
+ return result;
+ }
+
public static <T> List<T> filter(List<T> list, Predicate<? super T> predicate) {
ArrayList<T> filtered = new ArrayList<>(list.size());
list.forEach(
diff --git a/src/test/examplesAndroidO/invokecustom/TestGenerator.java b/src/test/examplesAndroidO/invokecustom/TestGenerator.java
index 9964ed0..a4cd89a 100644
--- a/src/test/examplesAndroidO/invokecustom/TestGenerator.java
+++ b/src/test/examplesAndroidO/invokecustom/TestGenerator.java
@@ -21,6 +21,8 @@
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
+// TODO(b/167145686): Migrate these tests to the new setup ala
+// InvokeDynamicVirtualDispatchToDefaultInterfaceMethodTest
public class TestGenerator {
private final Path classNamePath;
@@ -52,7 +54,6 @@
generateMethodTest6(cw);
generateMethodTest7(cw);
generateMethodTest8(cw);
- generateMethodTest9(cw);
generateMethodTest10(cw);
generateMethodTest11(cw);
generateMethodTest12(cw);
@@ -89,8 +90,6 @@
mv.visitMethodInsn(
Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test8", "()V", false);
mv.visitMethodInsn(
- Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test9", "()V", false);
- mv.visitMethodInsn(
Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test10", "()V", false);
mv.visitMethodInsn(
Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test11", "()V", false);
@@ -280,29 +279,6 @@
/**
* Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
- * MethodHandle of kind invoke virtual. The target method is a method into a class implementing
- * an abstract method and that shadows a default method from an interface.
- */
- private void generateMethodTest9(ClassVisitor cv) {
- MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test9", "()V",
- null, null);
- MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
- MethodType.class, MethodHandle.class);
- Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
- "bsmCreateCallSite", mt.toMethodDescriptorString(), false);
- mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
- mv.visitInsn(Opcodes.DUP);
- mv.visitMethodInsn(
- Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class), "<init>", "()V", false);
- mv.visitInvokeDynamicInsn("targetMethodTest10", "(Linvokecustom/InvokeCustom;)V", bootstrap,
- new Handle(Opcodes.H_INVOKEVIRTUAL, Type.getInternalName(InvokeCustom.class),
- "targetMethodTest10", "()V", false));
- mv.visitInsn(Opcodes.RETURN);
- mv.visitMaxs(-1, -1);
- }
-
- /**
- * Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
* MethodHandle of kind get static. The method handle read a static field from a class.
*/
private void generateMethodTest10(ClassVisitor cv) {
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index a92437f..11b41cc 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -200,7 +200,7 @@
b ->
b.addProguardConfiguration(
getProguardOptionsNPlus(enableProguardCompatibilityMode), Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaringnplus"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 2, "lambdadesugaringnplus"))
.run();
}
diff --git a/src/test/java/com/android/tools/r8/TestAppViewBuilder.java b/src/test/java/com/android/tools/r8/TestAppViewBuilder.java
new file mode 100644
index 0000000..a13b972
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/TestAppViewBuilder.java
@@ -0,0 +1,107 @@
+// 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;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.ProguardConfigurationRule;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public class TestAppViewBuilder {
+
+ private AndroidApp.Builder builder = AndroidApp.builder();
+ private List<Function<DexItemFactory, List<ProguardConfigurationRule>>> rules = new ArrayList<>();
+ private List<Consumer<InternalOptions>> optionModifications = new ArrayList<>();
+
+ public static TestAppViewBuilder builder() {
+ return new TestAppViewBuilder();
+ }
+
+ private TestAppViewBuilder() {}
+
+ public TestAppViewBuilder addProgramClasses(Class<?>... classes) {
+ return addProgramClasses(Arrays.asList(classes));
+ }
+
+ public TestAppViewBuilder addProgramClasses(Collection<Class<?>> classes) {
+ classes.forEach(clazz -> builder.addProgramFile(ToolHelper.getClassFileForTestClass(clazz)));
+ return this;
+ }
+
+ public TestAppViewBuilder addProgramClassFileData(byte[]... classes) {
+ return addProgramClassFileData(Arrays.asList(classes));
+ }
+
+ public TestAppViewBuilder addProgramClassFileData(Collection<byte[]> classes) {
+ builder.addClassProgramData(classes);
+ return this;
+ }
+
+ public TestAppViewBuilder addAndroidApp(AndroidApp app) {
+ app.getProgramResourceProviders().forEach(builder::addProgramResourceProvider);
+ app.getClasspathResourceProviders().forEach(builder::addClasspathResourceProvider);
+ app.getLibraryResourceProviders().forEach(builder::addLibraryResourceProvider);
+ assert !app.hasMainDexList() : "todo";
+ return this;
+ }
+
+ public TestAppViewBuilder addKeepAllRule() {
+ rules = null;
+ return this;
+ }
+
+ public TestAppViewBuilder addKeepMainRule(Class<?> mainClass) {
+ return addKeepRuleBuilder(
+ factory -> TestBase.buildKeepRuleForClassAndMethods(mainClass, factory));
+ }
+
+ public TestAppViewBuilder addKeepRuleBuilder(
+ Function<DexItemFactory, List<ProguardConfigurationRule>> ruleBuilder) {
+ if (rules != null) {
+ rules.add(ruleBuilder);
+ }
+ return this;
+ }
+
+ public TestAppViewBuilder addOptionsModification(Consumer<InternalOptions> optionsConsumer) {
+ optionModifications.add(optionsConsumer);
+ return this;
+ }
+
+ public AppView<AppInfoWithLiveness> buildWithLiveness() throws Exception {
+ return TestBase.computeAppViewWithLiveness(
+ builder.build(),
+ (rules == null
+ ? null
+ : factory ->
+ TestBase.buildConfigForRules(
+ factory, ListUtils.flatMap(rules, r -> r.apply(factory)))),
+ options -> optionModifications.forEach(consumer -> consumer.accept(options)));
+ }
+
+ public TestAppViewBuilder setMinApi(AndroidApiLevel minApi) {
+ optionModifications.add(options -> options.minApiLevel = minApi);
+ return this;
+ }
+
+ public TestAppViewBuilder addLibraryFiles(Path... files) {
+ return addLibraryFiles(Arrays.asList(files));
+ }
+
+ public TestAppViewBuilder addLibraryFiles(List<Path> files) {
+ builder.addLibraryFiles(files);
+ return this;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index f93398c..8c52813 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -775,17 +775,18 @@
protected static AppView<AppInfoWithLiveness> computeAppViewWithLiveness(AndroidApp app)
throws Exception {
- return computeAppViewWithLiveness(app, null, null);
+ return TestAppViewBuilder.builder().addAndroidApp(app).addKeepAllRule().buildWithLiveness();
}
protected static AppView<AppInfoWithLiveness> computeAppViewWithLiveness(
AndroidApp app, Class<?> mainClass) throws Exception {
- return computeAppViewWithLiveness(
- app,
- factory ->
- buildConfigForRules(factory, buildKeepRuleForClassAndMethods(mainClass, factory)));
+ return TestAppViewBuilder.builder()
+ .addAndroidApp(app)
+ .addKeepMainRule(mainClass)
+ .buildWithLiveness();
}
+ // We should try to get rid of this usage of keep rule building which is very internal.
protected static AppView<AppInfoWithLiveness> computeAppViewWithLiveness(
AndroidApp app, Function<DexItemFactory, ProguardConfiguration> keepConfig) throws Exception {
return computeAppViewWithLiveness(app, keepConfig, null);
diff --git a/src/test/java/com/android/tools/r8/TestParameters.java b/src/test/java/com/android/tools/r8/TestParameters.java
index 1df89b1..7e230ef 100644
--- a/src/test/java/com/android/tools/r8/TestParameters.java
+++ b/src/test/java/com/android/tools/r8/TestParameters.java
@@ -31,6 +31,8 @@
public boolean canUseDefaultAndStaticInterfaceMethods() {
assert isCfRuntime() || isDexRuntime();
+ assert !isCfRuntime() || apiLevel == null
+ : "Use canUseDefaultAndStaticInterfaceMethodsWhenDesugaring when using CF api levels.";
return isCfRuntime()
|| getApiLevel()
.isGreaterThanOrEqualTo(TestBase.apiLevelWithDefaultInterfaceMethodsSupport());
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java
index e997894..a580fb1 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java
@@ -6,7 +6,7 @@
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
import static com.android.tools.r8.utils.AndroidApiLevel.L_MR1;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbstract;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static junit.framework.TestCase.assertEquals;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -59,22 +59,22 @@
} else {
ClassSubject aSubject = inspector.clazz(A.class);
ClassSubject apiCaller = inspector.clazz(ApiCaller.class);
- assertThat(apiCaller, isPresent());
- MethodSubject callApiLevel = apiCaller.uniqueMethodWithName("callApiLevel");
if (parameters.isCfRuntime()) {
+ assert parameters.canUseDefaultAndStaticInterfaceMethods();
+ assertThat(apiCaller, isPresent());
assertThat(aSubject, isPresent());
+ MethodSubject callApiLevel = apiCaller.uniqueMethodWithName("callApiLevel");
assertThat(callApiLevel, CodeMatchers.invokesMethodWithName("apiLevel22"));
} else {
assert parameters.isDexRuntime();
- // TODO(b/191013385): A has a virtual method that calls callApiLevel on $CC, but
- // that call should be inlined.
- assertThat(aSubject, isPresent());
- assertThat(callApiLevel, isAbstract());
- ClassSubject classSubject = apiCaller.toCompanionClass();
- assertThat(classSubject, isPresent());
- assertEquals(1, classSubject.allMethods().size());
+ assert !parameters.canUseDefaultAndStaticInterfaceMethods();
+ assertThat(apiCaller, isAbsent());
+ assertThat(aSubject, isAbsent());
+ ClassSubject companionClass = apiCaller.toCompanionClass();
+ assertThat(companionClass, isPresent());
+ assertEquals(1, companionClass.allMethods().size());
assertThat(
- classSubject.allMethods().get(0),
+ companionClass.allMethods().get(0),
CodeMatchers.invokesMethodWithName("apiLevel22"));
}
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfLambdaTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfLambdaTest.java
index 0525cd5..04dc573 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfLambdaTest.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.utils.codeinspector.CodeMatchers;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.lang.reflect.Method;
+import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -47,6 +48,7 @@
@Test
public void testR8() throws Exception {
+ Assume.assumeTrue("b/197494749", parameters.canUseDefaultAndStaticInterfaceMethods());
Method apiMethod = Api.class.getDeclaredMethod("apiLevel22");
testForR8(parameters.getBackend())
.addProgramClasses(ApiCaller.class, Action.class, Main.class)
@@ -57,7 +59,6 @@
.apply(setMockApiLevelForMethod(apiMethod, L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.allowAccessModification()
- .noMinification()
.compile()
.inspect(
inspector -> {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java
index 1e2fd44..086cfa4 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java
@@ -30,7 +30,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public ApiModelNoInliningOfStaticInterfaceMethodsTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
index e7c5ce1..51e4d41 100644
--- a/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
@@ -7,6 +7,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.TestAppViewBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -20,6 +21,7 @@
import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.ProguardConfigurationRule;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.util.HashSet;
@@ -43,18 +45,18 @@
private AppView<AppInfoWithLiveness> computeAppViewWithLiveness(
Class<?> methodToBeKept, Class<?> classToBeKept) throws Exception {
- return computeAppViewWithLiveness(
- buildClasses(I.class, J.class, K.class, L.class, A.class, Main.class)
- .addLibraryFile(ToolHelper.getJava8RuntimeJar())
- .build(),
- factory ->
- buildConfigForRules(
- factory,
+ return TestAppViewBuilder.builder()
+ .addProgramClasses(I.class, J.class, K.class, L.class, A.class, Main.class)
+ .addLibraryFiles(ToolHelper.getJava8RuntimeJar())
+ .addKeepRuleBuilder(
+ factory ->
ImmutableList.<ProguardConfigurationRule>builder()
.addAll(buildKeepRuleForClassAndMethods(methodToBeKept, factory))
.addAll(buildKeepRuleForClass(classToBeKept, factory))
.addAll(buildKeepRuleForClassAndMethods(Main.class, factory))
- .build()));
+ .build())
+ .setMinApi(AndroidApiLevel.N)
+ .buildWithLiveness();
}
@Test
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndInterfaceMethodCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndInterfaceMethodCollisionTest.java
index f440689..837f374 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndInterfaceMethodCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndInterfaceMethodCollisionTest.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.NoUnusedInterfaceRemoval;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import org.junit.Test;
public class PrivateAndInterfaceMethodCollisionTest extends HorizontalClassMergingTestBase {
@@ -24,7 +23,15 @@
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
.addHorizontallyMergedClassesInspector(
- HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ i -> {
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ i.assertClassesMerged();
+ } else {
+ // With default method desugaring all uses of J::foo are eliminated so A and B
+ // merge.
+ i.assertIsCompleteMergeGroup(A.class, B.class);
+ }
+ })
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoUnusedInterfaceRemovalAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndInterfaceMethodCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndInterfaceMethodCollisionTest.java
index 48844ff..c21469c 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndInterfaceMethodCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndInterfaceMethodCollisionTest.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.NoUnusedInterfaceRemoval;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import org.junit.Test;
public class StaticAndInterfaceMethodCollisionTest extends HorizontalClassMergingTestBase {
@@ -24,7 +23,15 @@
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
.addHorizontallyMergedClassesInspector(
- HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ i -> {
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ i.assertNoClassesMerged();
+ } else {
+ // When desugaring the call to C.foo in main will be inlined to target the CC.
+ // With J.foo now unused the classes A and B are safe to merge.
+ i.assertIsCompleteMergeGroup(A.class, B.class);
+ }
+ })
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoUnusedInterfaceRemovalAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java
index e2eef5f..4e38b33 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java
@@ -30,7 +30,9 @@
.addHorizontallyMergedClassesInspector(
inspector ->
inspector
- .assertIsCompleteMergeGroup(I.class, J.class)
+ .applyIf(
+ parameters.canUseDefaultAndStaticInterfaceMethods(),
+ i -> i.assertIsCompleteMergeGroup(I.class, J.class))
.applyIf(
!parameters.canUseDefaultAndStaticInterfaceMethods(),
i -> i.assertIsCompleteMergeGroup(B1.class, B2.class))
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java
index 0af1e85..f7f2e0d 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.classmerging.horizontal.HorizontalClassMergingTestBase;
import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import org.junit.Assume;
import org.junit.Test;
public class OverrideDefaultMethodTest extends HorizontalClassMergingTestBase {
@@ -23,6 +24,7 @@
@Test
public void testR8() throws Exception {
+ Assume.assumeTrue("b/197494749", parameters.canUseDefaultAndStaticInterfaceMethods());
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java
index 0ea8597..313dec7 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java
@@ -38,11 +38,17 @@
inspector.assertNoClassesMerged();
} else {
inspector
- .assertClassesNotMerged(A.class, B.class)
.assertIsCompleteMergeGroup(I.class, J.class)
- .assertIsCompleteMergeGroup(
- SyntheticItemsTestUtils.syntheticCompanionClass(I.class),
- SyntheticItemsTestUtils.syntheticCompanionClass(J.class))
+ .applyIf(
+ parameters.canUseDefaultAndStaticInterfaceMethods(),
+ i ->
+ i.assertClassesNotMerged(A.class, B.class)
+ .assertIsCompleteMergeGroup(
+ SyntheticItemsTestUtils.syntheticCompanionClass(I.class),
+ SyntheticItemsTestUtils.syntheticCompanionClass(J.class)))
+ .applyIf(
+ !parameters.canUseDefaultAndStaticInterfaceMethods(),
+ i -> i.assertClassesMerged(A.class, B.class))
.assertNoOtherClassesMerged();
}
})
@@ -56,7 +62,9 @@
onlyIf(parameters.canUseDefaultAndStaticInterfaceMethods(), isPresent()));
assertThat(codeInspector.clazz(Parent.class), isPresent());
assertThat(codeInspector.clazz(A.class), isPresent());
- assertThat(codeInspector.clazz(B.class), isPresent());
+ assertThat(
+ codeInspector.clazz(B.class),
+ onlyIf(parameters.canUseDefaultAndStaticInterfaceMethods(), isPresent()));
assertThat(codeInspector.clazz(C.class), isPresent());
});
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java
index 6f5e761..1ec170d 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.classmerging.horizontal.dispatch;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -12,7 +13,6 @@
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.classmerging.horizontal.HorizontalClassMergingTestBase;
-import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import org.junit.Test;
public class OverrideMergeAbsentTest extends HorizontalClassMergingTestBase {
@@ -30,14 +30,23 @@
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.addHorizontallyMergedClassesInspector(
- HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ inspector -> {
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ inspector.assertNoClassesMerged();
+ } else {
+ // When desugaring B.m is moved and A and B can be merged.
+ inspector.assertIsCompleteMergeGroup(A.class, B.class);
+ }
+ })
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("A", "B", "A", "J")
.inspect(
codeInspector -> {
assertThat(codeInspector.clazz(J.class), isPresent());
assertThat(codeInspector.clazz(A.class), isPresent());
- assertThat(codeInspector.clazz(B.class), isPresent());
+ assertThat(
+ codeInspector.clazz(B.class),
+ parameters.canUseDefaultAndStaticInterfaceMethods() ? isPresent() : isAbsent());
assertThat(codeInspector.clazz(C.class), isPresent());
});
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java
index cc19dd2..6529d20 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.classmerging.horizontal.interfaces;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -55,11 +56,10 @@
.assertMergedInto(B.class, A.class);
if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
inspector.assertClassesNotMerged(I.class, J.class, K.class);
- } else {
- inspector
- .assertIsCompleteMergeGroup(I.class, J.class)
- .assertClassesNotMerged(K.class);
+ } else if (enableInterfaceMergingInInitial) {
+ inspector.assertIsCompleteMergeGroup(I.class, J.class);
}
+ inspector.assertNoOtherClassesMerged();
})
.addOptionsModification(
options -> {
@@ -82,14 +82,12 @@
assertThat(aClassSubject, isImplementing(inspector.clazz(K.class)));
ClassSubject cClassSubject = inspector.clazz(C.class);
- assertThat(cClassSubject, isPresent());
- assertThat(
- cClassSubject,
- isImplementing(
- inspector.clazz(
- parameters.canUseDefaultAndStaticInterfaceMethods()
- ? J.class
- : I.class)));
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ assertThat(cClassSubject, isPresent());
+ assertThat(cClassSubject, isImplementing(inspector.clazz(J.class)));
+ } else {
+ assertThat(cClassSubject, isAbsent());
+ }
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("A", "K", "J");
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java
index e18ce93..5ee0365 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.classmerging.horizontal.interfaces;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -16,7 +17,9 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -37,6 +40,7 @@
@Test
public void test() throws Exception {
+ Assume.assumeTrue("b/197494749", parameters.canUseDefaultAndStaticInterfaceMethods());
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
@@ -47,7 +51,10 @@
if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
inspector.assertNoClassesMerged();
} else {
- inspector.assertIsCompleteMergeGroup(I.class, J.class);
+ // J is removed as part of desugaring. This enables merging of its CC class.
+ inspector.assertIsCompleteMergeGroup(
+ SyntheticItemsTestUtils.syntheticCompanionClass(J.class),
+ SyntheticItemsTestUtils.syntheticCompanionClass(K.class));
}
})
.enableInliningAnnotations()
@@ -60,19 +67,18 @@
.inspect(
inspector -> {
ClassSubject aClassSubject = inspector.clazz(A.class);
- assertThat(aClassSubject, isPresent());
- assertThat(aClassSubject, isImplementing(inspector.clazz(I.class)));
- assertThat(aClassSubject, isImplementing(inspector.clazz(K.class)));
-
ClassSubject bClassSubject = inspector.clazz(B.class);
- assertThat(bClassSubject, isPresent());
- assertThat(
- bClassSubject,
- isImplementing(
- inspector.clazz(
- parameters.canUseDefaultAndStaticInterfaceMethods()
- ? J.class
- : I.class)));
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ assertThat(aClassSubject, isPresent());
+ assertThat(aClassSubject, isImplementing(inspector.clazz(I.class)));
+ assertThat(aClassSubject, isImplementing(inspector.clazz(K.class)));
+ assertThat(bClassSubject, isPresent());
+ assertThat(bClassSubject, isImplementing(inspector.clazz(J.class)));
+ } else {
+ // When desugaring the calls in main will directly target the CC classes.
+ assertThat(aClassSubject, isAbsent());
+ assertThat(bClassSubject, isAbsent());
+ }
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("K", "J");
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java
index 2324f03..a1dbbaf 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.classmerging.horizontal.interfaces;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -61,11 +62,15 @@
.inspect(
inspector -> {
ClassSubject aClassSubject = inspector.clazz(A.class);
- assertThat(aClassSubject, isPresent());
- if (parameters.isCfRuntime()) {
- assertThat(aClassSubject, isImplementing(inspector.clazz(I.class)));
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ assertThat(aClassSubject, isPresent());
+ if (parameters.isCfRuntime()) {
+ assertThat(aClassSubject, isImplementing(inspector.clazz(I.class)));
+ } else {
+ assertThat(aClassSubject, isImplementing(inspector.clazz(J.class)));
+ }
} else {
- assertThat(aClassSubject, isImplementing(inspector.clazz(J.class)));
+ assertThat(aClassSubject, isAbsent());
}
})
.run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java
index 5a656d9..2c40a58 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.classmerging.horizontal.interfaces;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -40,7 +41,13 @@
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
.addHorizontallyMergedClassesInspector(
- inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
+ inspector -> {
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ inspector.assertIsCompleteMergeGroup(I.class, J.class);
+ } else {
+ inspector.assertNoClassesMerged();
+ }
+ })
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoUnusedInterfaceRemovalAnnotations()
@@ -50,8 +57,12 @@
.inspect(
inspector -> {
ClassSubject aClassSubject = inspector.clazz(A.class);
- assertThat(aClassSubject, isPresent());
- assertThat(aClassSubject, isImplementing(inspector.clazz(I.class)));
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ assertThat(aClassSubject, isPresent());
+ assertThat(aClassSubject, isImplementing(inspector.clazz(I.class)));
+ } else {
+ assertThat(aClassSubject, isAbsent());
+ }
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("I", "J");
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java
index a84dbac..f68dd90 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.classmerging.horizontal.interfaces;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -17,6 +18,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -42,13 +44,7 @@
.addKeepMainRule(Main.class)
// I and J are not eligible for merging, since they declare the same default method.
.addHorizontallyMergedClassesInspector(
- inspector -> {
- if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
- inspector.assertNoClassesMerged();
- } else {
- inspector.assertIsCompleteMergeGroup(I.class, J.class);
- }
- })
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
@@ -59,10 +55,17 @@
.inspect(
inspector -> {
ClassSubject aClassSubject = inspector.clazz(A.class);
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+ if (!parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ // When desugaring, the forwarding methods to the CC.m methods will be inlined and
+ // the class instances become dead code.
+ assertThat(aClassSubject, isAbsent());
+ assertThat(bClassSubject, isAbsent());
+ return;
+ }
assertThat(aClassSubject, isPresent());
assertThat(aClassSubject, isImplementing(inspector.clazz(I.class)));
- ClassSubject bClassSubject = inspector.clazz(B.class);
assertThat(bClassSubject, isPresent());
assertThat(
bClassSubject,
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index 2f157d0..3b35a12 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -50,6 +50,7 @@
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
+import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -355,6 +356,7 @@
@Test
public void testNestedDefaultInterfaceMethodsTest() throws Throwable {
+ Assume.assumeTrue("b/197494749", parameters.canUseDefaultAndStaticInterfaceMethods());
String main = "classmerging.NestedDefaultInterfaceMethodsTest";
Path[] programFiles =
new Path[] {
diff --git a/src/test/java/com/android/tools/r8/code/invokedynamic/InvokeDynamicVirtualDispatchToDefaultInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/code/invokedynamic/InvokeDynamicVirtualDispatchToDefaultInterfaceMethodTest.java
new file mode 100644
index 0000000..7494b13
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/code/invokedynamic/InvokeDynamicVirtualDispatchToDefaultInterfaceMethodTest.java
@@ -0,0 +1,144 @@
+// 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.code.invokedynamic;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.StringUtils;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Test invokedynamic with a static bootstrap method with an extra arg that is a MethodHandle of
+ * kind invoke virtual. The target method is a method into a class implementing an abstract method
+ * and that shadows a default method from an interface.
+ */
+// TODO(b/167145686): Copy this test and implement all of the variants in
+// ...AndroidOTest.invokeCustom... and then delete those tests.
+@RunWith(Parameterized.class)
+public class InvokeDynamicVirtualDispatchToDefaultInterfaceMethodTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("Called I.foo");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withAllRuntimes()
+ .withApiLevelsStartingAtIncluding(apiLevelWithInvokeCustomSupport())
+ .enableApiLevelsForCf()
+ .build();
+ }
+
+ public InvokeDynamicVirtualDispatchToDefaultInterfaceMethodTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForDesugaring(parameters)
+ .addProgramClasses(I.class, A.class)
+ .addProgramClassFileData(getTransformedTestClass())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assumeTrue(
+ "Only test one R8/CF build.",
+ parameters.isDexRuntime() || parameters.getApiLevel() == apiLevelWithInvokeCustomSupport());
+ testForR8(parameters.getBackend())
+ .allowAccessModification()
+ .addProgramClasses(I.class, A.class)
+ .addProgramClassFileData(getTransformedTestClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(TestClass.class)
+ .addKeepRules("-keepclassmembers class * { *** foo(...); }")
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ private byte[] getTransformedTestClass() throws Exception {
+ ClassReference aClass = Reference.classFromClass(A.class);
+ MethodReference iFoo = Reference.methodFromMethod(I.class.getDeclaredMethod("foo"));
+ MethodReference bsm =
+ Reference.methodFromMethod(
+ TestClass.class.getDeclaredMethod(
+ "bsmCreateCallSite",
+ Lookup.class,
+ String.class,
+ MethodType.class,
+ MethodHandle.class));
+ return transformer(TestClass.class)
+ .transformMethodInsnInMethod(
+ "main",
+ (opcode, owner, name, descriptor, isInterface, visitor) -> {
+ if (name.equals("replaced")) {
+ visitor.visitInvokeDynamicInsn(
+ iFoo.getMethodName(),
+ "(" + aClass.getDescriptor() + ")V",
+ new Handle(
+ Opcodes.H_INVOKESTATIC,
+ bsm.getHolderClass().getBinaryName(),
+ bsm.getMethodName(),
+ bsm.getMethodDescriptor(),
+ false),
+ new Handle(
+ Opcodes.H_INVOKEVIRTUAL,
+ aClass.getBinaryName(),
+ iFoo.getMethodName(),
+ iFoo.getMethodDescriptor(),
+ false));
+ } else {
+ visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ }
+ })
+ .transform();
+ }
+
+ public interface I {
+
+ default void foo() {
+ System.out.println("Called I.foo");
+ }
+ }
+
+ public static class A implements I {
+ // Instantiation with default from I.
+ }
+
+ static class TestClass {
+
+ public static CallSite bsmCreateCallSite(
+ MethodHandles.Lookup caller, String name, MethodType type, MethodHandle handle)
+ throws Throwable {
+ return new ConstantCallSite(handle);
+ }
+
+ public static void replaced(Object o) {
+ throw new RuntimeException("unreachable!");
+ }
+
+ public static void main(String[] args) {
+ replaced(new A());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java
index 7cde792..2e9abfb 100644
--- a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java
+++ b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java
@@ -100,9 +100,9 @@
testForD8()
.addProgramClassesAndInnerClasses(CLASS)
.setMinApiThreshold(AndroidApiLevel.K)
- .compile();
+ .compile()
+ .assertNoMessages();
compileResult
- // TODO(b/123506120): Add .assertNoMessages()
.run(parameters.getRuntime(), CLASS)
.assertSuccessWithOutput(EXPECTED);
runDebugger(compileResult.debugConfig(), true);
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarInnerClassesInInterfaces.java b/src/test/java/com/android/tools/r8/desugar/DesugarInnerClassesInInterfaces.java
index 1c8d5b9..8902cd2 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugarInnerClassesInInterfaces.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarInnerClassesInInterfaces.java
@@ -4,11 +4,13 @@
package com.android.tools.r8.desugar;
import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringForTesting.getCompanionClassNameSuffix;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.DesugarTestConfiguration;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.concurrent.Callable;
@@ -30,13 +32,6 @@
WithLocalInner.class.getName() + getCompanionClassNameSuffix(),
"true");
- private final List<String> EXPECTED_RESULT_WITH_DESUGARING_B168697955 =
- ImmutableList.of(
- WithAnonymousInner.class.getName() + getCompanionClassNameSuffix(),
- "false",
- WithLocalInner.class.getName() + getCompanionClassNameSuffix(),
- "false");
-
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
@@ -69,6 +64,7 @@
@Test
public void testR8Compat() throws Exception {
+ assumeTrue(parameters.isDexRuntime() || parameters.getApiLevel() == AndroidApiLevel.B);
testForR8Compat(parameters.getBackend())
.addInnerClasses(DesugarInnerClassesInInterfaces.class)
.setMinApi(parameters.getApiLevel())
@@ -78,37 +74,14 @@
.compile()
.run(parameters.getRuntime(), TestClass.class)
.applyIf(
- parameters.canUseDefaultAndStaticInterfaceMethods(),
+ parameters.isCfRuntime() || parameters.canUseDefaultAndStaticInterfaceMethods(),
result -> result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITHOUT_DESUGARING),
- // The static method which is moved to the companion class is inlined and causing
- // this different output. The rule "-keep class * { *; }" does not keep the static
- // method from being inlined after it has moved. Turning off inlining produces the
- // expected result. The inlining cause the getEnclosingClass() to return null.
- // See b/168697955.
- result ->
- result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITH_DESUGARING_B168697955));
- }
-
- @Test
- public void testR8_B168697955() throws Exception {
- testForR8(parameters.getBackend())
- .addInnerClasses(DesugarInnerClassesInInterfaces.class)
- .setMinApi(parameters.getApiLevel())
- .addKeepAllClassesRule()
- .addKeepAttributeInnerClassesAndEnclosingMethod()
- // With inlining turned off we get the expected result.
- .addOptionsModification(options -> options.enableInlining = false)
- .compile()
- .run(parameters.getRuntime(), TestClass.class)
- .applyIf(
- parameters.canUseDefaultAndStaticInterfaceMethods(),
- result -> result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITHOUT_DESUGARING),
- // TODO(b/187377562): We remove the attribute due to not pinning the moved methods.
- result -> result.assertFailureWithErrorThatThrows(NullPointerException.class));
+ result -> result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITH_DESUGARING));
}
@Test
public void testR8Full() throws Exception {
+ assumeTrue(parameters.isDexRuntime() || parameters.getApiLevel() == AndroidApiLevel.B);
testForR8(parameters.getBackend())
.addInnerClasses(DesugarInnerClassesInInterfaces.class)
.setMinApi(parameters.getApiLevel())
@@ -117,10 +90,9 @@
.compile()
.run(parameters.getRuntime(), TestClass.class)
.applyIf(
- parameters.canUseDefaultAndStaticInterfaceMethods(),
+ parameters.isCfRuntime() || parameters.canUseDefaultAndStaticInterfaceMethods(),
result -> result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITHOUT_DESUGARING),
- // TODO(b/187377562): We remove the attribute due to not pinning the moved methods.
- result -> result.assertFailureWithErrorThatThrows(NullPointerException.class));
+ result -> result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITH_DESUGARING));
}
interface WithAnonymousInner {
diff --git a/src/test/java/com/android/tools/r8/desugar/enclosingmethod/EnclosingMethodRewriteTest.java b/src/test/java/com/android/tools/r8/desugar/enclosingmethod/EnclosingMethodRewriteTest.java
index e7cc9c9..9a47de9 100644
--- a/src/test/java/com/android/tools/r8/desugar/enclosingmethod/EnclosingMethodRewriteTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/enclosingmethod/EnclosingMethodRewriteTest.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.TestBase;
@@ -73,8 +72,6 @@
"interface " + A.class.getTypeName(), "public int " + A.class.getTypeName() + ".def()", "42"
};
- private final String[] EXPECTED_FULL = new String[] {"null", "null", "42"};
-
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
@@ -102,7 +99,7 @@
.addKeepAttributeInnerClassesAndEnclosingMethod()
.setMinApi(parameters.getApiLevel())
.compile()
- .inspect(inspector -> inspect(inspector, true))
+ .inspect(this::inspect)
.run(parameters.getRuntime(), MAIN)
.applyIf(
parameters.canUseDefaultAndStaticInterfaceMethods(),
@@ -126,8 +123,7 @@
.addKeepAttributeInnerClassesAndEnclosingMethod()
.setMinApi(parameters.getApiLevel())
.compile()
- .inspect(
- inspector -> inspect(inspector, parameters.canUseDefaultAndStaticInterfaceMethods()))
+ .inspect(this::inspect)
.run(parameters.getRuntime(), MAIN)
.applyIf(
parameters.canUseDefaultAndStaticInterfaceMethods(),
@@ -139,25 +135,21 @@
result.assertSuccessWithOutputLines(EXPECTED);
}
},
- result -> result.assertSuccessWithOutputLines(EXPECTED_FULL));
+ result -> result.assertSuccessWithOutputLines(EXPECTED_CC));
}
- private void inspect(CodeInspector inspector, boolean hasEnclosingMethod) {
+ private void inspect(CodeInspector inspector) {
ClassSubject cImplSubject = inspector.clazz(A.class.getTypeName() + "$1");
assertThat(cImplSubject, isPresent());
ClassSubject enclosingClassSubject =
parameters.canUseDefaultAndStaticInterfaceMethods()
? inspector.clazz(A.class.getTypeName())
- : inspector.clazz(A.class.getTypeName() + "$-CC");
+ : inspector.clazz(A.class.getTypeName()).toCompanionClass();
assertThat(enclosingClassSubject, isPresent());
EnclosingMethodAttribute enclosingMethodAttribute =
cImplSubject.getDexProgramClass().getEnclosingMethodAttribute();
- if (hasEnclosingMethod) {
- assertEquals(
- enclosingClassSubject.getDexProgramClass().getType(),
- enclosingMethodAttribute.getEnclosingMethod().getHolderType());
- } else {
- assertNull(enclosingMethodAttribute);
- }
+ assertEquals(
+ enclosingClassSubject.getDexProgramClass().getType(),
+ enclosingMethodAttribute.getEnclosingMethod().getHolderType());
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InvokeStaticDesugarTest.java b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InvokeStaticDesugarTest.java
index 35e52b4..8cd35aa 100644
--- a/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InvokeStaticDesugarTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InvokeStaticDesugarTest.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.desugar.staticinterfacemethod.InvokeStaticDesugarTest.Library.foo;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
@@ -151,12 +152,14 @@
private Set<FoundMethodSubject> getSyntheticMethods(CodeInspector inspector) {
Set<FoundMethodSubject> methods = new HashSet<>();
- assert inspector.allClasses().stream()
- .allMatch(
+ inspector
+ .allClasses()
+ .forEach(
c ->
- !SyntheticItemsTestUtils.isExternalSynthetic(c.getFinalReference())
- || SyntheticItemsTestUtils.isExternalStaticInterfaceCall(
- c.getFinalReference()));
+ assertTrue(
+ !SyntheticItemsTestUtils.isExternalSynthetic(c.getFinalReference())
+ || SyntheticItemsTestUtils.isExternalStaticInterfaceCall(
+ c.getFinalReference())));
inspector.allClasses().stream()
.filter(c -> SyntheticItemsTestUtils.isExternalStaticInterfaceCall(c.getFinalReference()))
.forEach(c -> methods.addAll(c.allMethods(m -> !m.isInstanceInitializer())));
diff --git a/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/MutuallyRecursiveMethodsTest.java b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/MutuallyRecursiveMethodsTest.java
index ee911d3..dfbbd6e 100644
--- a/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/MutuallyRecursiveMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/MutuallyRecursiveMethodsTest.java
@@ -35,6 +35,16 @@
.assertSuccessWithOutput(EXPECTED);
}
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(MutuallyRecursiveMethodsTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
interface I {
static boolean isEven(int i) {
return i == 0 || isOdd(i - 1);
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithPublicStaticResolutionInvokeVirtualTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithPublicStaticResolutionInvokeVirtualTest.java
index 12869e2..c960b33 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithPublicStaticResolutionInvokeVirtualTest.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithPublicStaticResolutionInvokeVirtualTest.java
@@ -102,22 +102,21 @@
private void checkResult(TestRunResult<?> result, boolean isR8) {
// Invalid invoke case is where the invoke-virtual targets C.m.
if (invalidInvoke) {
- if (parameters.isCfRuntime()) {
- result.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
- return;
- }
- if (parameters.getDexRuntimeVersion().isInRangeInclusive(Version.V5_1_1, Version.V7_0_0)) {
+ if (parameters.isDexRuntimeVersion(Version.V7_0_0)
+ && parameters.canUseDefaultAndStaticInterfaceMethodsWhenDesugaring()) {
+ // The v7 VM incorrectly fails to throw.
result.assertSuccessWithOutput(EXPECTED);
- return;
+ } else {
+ result.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
}
- result.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
return;
}
if (isR8
&& parameters.isDexRuntime()
&& parameters.getDexRuntimeVersion().isNewerThan(Version.V6_0_1)
- && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0)) {
+ && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0)
+ && parameters.canUseDefaultAndStaticInterfaceMethodsWhenDesugaring()) {
// TODO(b/182255398): This should be EXPECTED.
result.assertSuccessWithOutput(EXPECTED_R8);
return;
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithPublicStaticResolutionOnClassTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithPublicStaticResolutionOnClassTest.java
index 1684c5a..b5949f7 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithPublicStaticResolutionOnClassTest.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithPublicStaticResolutionOnClassTest.java
@@ -70,10 +70,9 @@
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), TestClass.class)
- // TODO(b/182335909): Ideally, this should also throw ICCE when desugaring.
.applyIf(
- !parameters.canUseDefaultAndStaticInterfaceMethodsWhenDesugaring()
- || parameters.isDexRuntimeVersion(Version.V7_0_0),
+ parameters.canUseDefaultAndStaticInterfaceMethodsWhenDesugaring()
+ && parameters.isDexRuntimeVersion(Version.V7_0_0),
r -> r.assertSuccessWithOutput(EXPECTED_INVALID),
r -> r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class));
}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest.java
index 698cf50..f4fec42 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -36,6 +37,11 @@
}
@Test
+ @Ignore("b/197494749 and b/120130831")
+ // TODO(b/197494749): Update this test now that desugaring is moved up. In particular this must
+ // be rewritten with CF based transformers as R8 does not support interface desugaring on DEX.
+ // TODO(b/120130831): With the move of desugaring this issue should be resolved. The bug indicates
+ // that a workaround for issues related to the IR implementation can now be reverted.
public void test() throws Exception {
// Note that the expected output is independent of the presence of J.m().
String expectedOutput = StringUtils.lines("I.m()", "JImpl.m()");
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/InterfaceEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/InterfaceEnumUnboxingTest.java
index 2f27855..3bc5c02 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/InterfaceEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/InterfaceEnumUnboxingTest.java
@@ -59,9 +59,12 @@
SuccessUnusedDefaultMethod.EnumInterface.class,
SuccessUnusedDefaultMethodOverride.EnumInterface.class,
SuccessUnusedDefaultMethodOverrideEnum.EnumInterface.class)
- .assertNotUnboxed(
- FailureDefaultMethodUsed.EnumInterface.class,
- FailureUsedAsInterface.EnumInterface.class))
+ .assertNotUnboxed(FailureUsedAsInterface.EnumInterface.class)
+ // When desugaring interfaces the dispatch will inline the forwarding method
+ // to the CC method allowing unboxing.
+ .assertUnboxedIf(
+ !parameters.canUseDefaultAndStaticInterfaceMethods(),
+ FailureDefaultMethodUsed.EnumInterface.class))
.noMinification()
.enableNoVerticalClassMergingAnnotations()
.enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java
index 509bfa2..99ab7b3 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java
@@ -38,9 +38,9 @@
.addProgramClasses(I.class)
.addProgramClassFileData(getClassWithTransformedInvoked())
.run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrowsIf(parameters.isCfRuntime(), VerifyError.class)
// TODO(b/144410139): Consider making this a compilation failure when generating DEX.
- .assertSuccessWithOutputLinesIf(parameters.isDexRuntime(), "Hello World!")
- .assertFailureWithErrorThatThrowsIf(parameters.isCfRuntime(), VerifyError.class);
+ .assertSuccessWithOutputLinesIf(parameters.isDexRuntime(), "Hello World!");
}
@Test
@@ -52,11 +52,14 @@
.addKeepMainRule(Main.class)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertFailureWithErrorThatThrows(
- parameters.isCfRuntime()
- ? VerifyError.class
- // TODO(b/144410139): Consider making this a compilation failure.
- : NullPointerException.class);
+ .assertFailureWithErrorThatThrowsIf(parameters.isCfRuntime(), VerifyError.class)
+ // TODO(b/144410139): Consider making this a compilation failure when generating DEX.
+ .assertSuccessWithOutputLinesIf(
+ parameters.isDexRuntime() && !parameters.canUseDefaultAndStaticInterfaceMethods(),
+ "Hello World!")
+ .assertFailureWithErrorThatThrowsIf(
+ parameters.isDexRuntime() && parameters.canUseDefaultAndStaticInterfaceMethods(),
+ NullPointerException.class);
}
private byte[] getClassWithTransformedInvoked() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/graph/invokevirtual/InvokeVirtualPrivateBaseWithDefaultDirectInvokeTest.java b/src/test/java/com/android/tools/r8/graph/invokevirtual/InvokeVirtualPrivateBaseWithDefaultDirectInvokeTest.java
index f989728..a8a222d 100644
--- a/src/test/java/com/android/tools/r8/graph/invokevirtual/InvokeVirtualPrivateBaseWithDefaultDirectInvokeTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokevirtual/InvokeVirtualPrivateBaseWithDefaultDirectInvokeTest.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfVm;
-import com.android.tools.r8.utils.AndroidApiLevel;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -38,10 +37,7 @@
testForJvm()
.addInnerClasses(getClass())
.run(parameters.getRuntime(), Main.class)
- .applyIf(
- parameters.isCfRuntime(CfVm.JDK11),
- r -> r.assertSuccessWithOutputLines("I::foo"),
- r -> r.assertFailureWithErrorThatThrows(IllegalAccessError.class));
+ .apply(r -> assertResultIsCorrect(r, true));
}
@Test
@@ -50,7 +46,7 @@
.addInnerClasses(getClass())
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .apply(this::assertResultIsCorrect);
+ .apply(r -> assertResultIsCorrect(r, false));
}
@Test
@@ -63,17 +59,23 @@
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
// TODO(b/182189123): This should have the same behavior as D8.
- .assertSuccessWithOutputLines("I::foo");
+ .applyIf(
+ parameters.isCfRuntime(),
+ r -> r.assertSuccessWithOutputLines("I::foo"),
+ r -> assertResultIsCorrect(r, true));
}
- public void assertResultIsCorrect(SingleTestRunResult<?> result) {
- if (parameters.isCfRuntime(CfVm.JDK11)
- && parameters.getApiLevel().isGreaterThan(AndroidApiLevel.M)) {
+ public void assertResultIsCorrect(SingleTestRunResult<?> result, boolean nonDesugaredCf) {
+ boolean isNotDesugared =
+ (nonDesugaredCf && parameters.isCfRuntime())
+ || parameters.canUseDefaultAndStaticInterfaceMethodsWhenDesugaring();
+ // JDK 11 allows this incorrect dispatch for some reason.
+ if (parameters.isCfRuntime(CfVm.JDK11) && isNotDesugared) {
result.assertSuccessWithOutputLines("I::foo");
return;
}
- // TODO(b/152199517): Should be illegal access for DEX.
- if (parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThan(AndroidApiLevel.M)) {
+ // TODO(b/152199517): Should become an illegal access on future DEX VM.
+ if (parameters.isDexRuntime() && isNotDesugared) {
result.assertSuccessWithOutputLines("I::foo");
return;
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromDefaultInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromDefaultInterfaceMethodTest.java
index 9b3dd42..baf0868 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromDefaultInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromDefaultInterfaceMethodTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize.outliner;
import static com.android.tools.r8.references.Reference.methodFromMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -16,11 +17,9 @@
import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -65,25 +64,22 @@
}
private void inspect(CodeInspector inspector) {
- ClassSubject interfaceSubject;
- MethodSubject greetMethodSubject;
- if (parameters.isCfRuntime()
- || parameters
- .getApiLevel()
- .isGreaterThanOrEqualTo(apiLevelWithDefaultInterfaceMethodsSupport())) {
- interfaceSubject = inspector.clazz(I.class);
- greetMethodSubject = interfaceSubject.uniqueMethodWithName("greet");
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ ClassSubject interfaceSubject = inspector.clazz(I.class);
+ MethodSubject greetMethodSubject = interfaceSubject.uniqueMethodWithName("greet");
+ assertThat(interfaceSubject, isPresent());
+ assertThat(greetMethodSubject, isPresent());
+ assertEquals(
+ 1,
+ greetMethodSubject
+ .streamInstructions()
+ .filter(InstructionSubject::isInvokeStatic)
+ .count());
} else {
- interfaceSubject = inspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(I.class));
- List<FoundMethodSubject> companionMethods = interfaceSubject.allMethods();
- assertEquals(1, companionMethods.size());
- greetMethodSubject = companionMethods.get(0);
+ // The companion class method is inlined into main.
+ assertThat(
+ inspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(I.class)), isAbsent());
}
- assertThat(interfaceSubject, isPresent());
- assertThat(greetMethodSubject, isPresent());
- assertEquals(
- 1,
- greetMethodSubject.streamInstructions().filter(InstructionSubject::isInvokeStatic).count());
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
index 6bc0862..c10d1db 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize.unusedinterfaces;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -57,7 +58,9 @@
assertThat(iClassSubject, isPresent());
ClassSubject jClassSubject = inspector.clazz(J.class);
- assertThat(jClassSubject, isPresent());
+ assertThat(
+ jClassSubject,
+ parameters.canUseDefaultAndStaticInterfaceMethods() ? isPresent() : isAbsent());
ClassSubject aClassSubject = inspector.clazz(A.class);
assertThat(aClassSubject, isPresent());
@@ -66,7 +69,9 @@
// m() that happens to be used.
assertEquals(1, aClassSubject.getDexProgramClass().interfaces.size());
assertEquals(
- jClassSubject.getDexProgramClass().type,
+ parameters.canUseDefaultAndStaticInterfaceMethods()
+ ? jClassSubject.getDexProgramClass().type
+ : iClassSubject.getDexProgramClass().type,
aClassSubject.getDexProgramClass().interfaces.values[0]);
}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceDefaultBridgeTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceDefaultBridgeTest.java
index 38eed54..e11bdfe 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceDefaultBridgeTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceDefaultBridgeTest.java
@@ -4,9 +4,9 @@
package com.android.tools.r8.memberrebinding;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverInline;
@@ -17,7 +17,6 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -60,13 +59,7 @@
assertTrue(clazz.allMethods().isEmpty());
if (!parameters.canUseDefaultAndStaticInterfaceMethods()) {
ClassSubject classSubject = clazz.toCompanionClass();
- assertThat(classSubject, isPresent());
- // TODO(b/197851381): We should be able to remove the bridge but it is problematic
- // since this require rewriting the call sites. Moving desugaring to the enqueuer
- // will also fix this.
- assertEquals(2, classSubject.allMethods().size());
- assertTrue(
- classSubject.allMethods().stream().anyMatch(FoundMethodSubject::isBridge));
+ assertThat(classSubject, isAbsent());
}
});
}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/desugar/DefaultInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/desugar/DefaultInterfaceMethodTest.java
index 1868602..18f0c96 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/desugar/DefaultInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/desugar/DefaultInterfaceMethodTest.java
@@ -3,16 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming.applymapping.desugar;
-import static com.android.tools.r8.references.Reference.classFromClass;
-import static org.junit.Assert.assertTrue;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringForTesting;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -73,26 +72,21 @@
@Test
public void testLibraryLinkedWithProgram() throws Throwable {
- String ruleContent = "-keep class " + LibraryInterface.class.getTypeName() + " { *; }";
R8TestCompileResult libraryResult =
testForR8(parameters.getBackend())
.addProgramClasses(LibraryInterface.class)
- .addKeepRules(ruleContent)
+ .addKeepClassAndMembersRules(LibraryInterface.class)
.setMinApi(parameters.getApiLevel())
.compile();
CodeInspector inspector = libraryResult.inspector();
- assertTrue(inspector.clazz(LibraryInterface.class).isPresent());
- assertTrue(inspector.method(LibraryInterface.class.getMethod("foo")).isPresent());
- if (willDesugarDefaultInterfaceMethods(parameters.getApiLevel())) {
+ assertThat(inspector.clazz(LibraryInterface.class), isPresent());
+ assertThat(inspector.method(LibraryInterface.class.getMethod("foo")), isPresent());
+ if (!parameters.canUseDefaultAndStaticInterfaceMethods()) {
ClassSubject companion =
- inspector.clazz(
- Reference.classFromDescriptor(
- InterfaceDesugaringForTesting.getCompanionClassDescriptor(
- classFromClass(LibraryInterface.class).getDescriptor())));
- // Check that we included the companion class.
- assertTrue(companion.isPresent());
- // TODO(b/129223905): Check the method is also present on the companion class.
- assertTrue(inspector.method(LibraryInterface.class.getMethod("foo")).isPresent());
+ inspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(LibraryInterface.class));
+ // Check that we included the companion class and method.
+ assertThat(companion, isPresent());
+ assertEquals(1, companion.allMethods().size());
}
testForR8(parameters.getBackend())
@@ -107,8 +101,4 @@
.run(parameters.getRuntime(), ProgramClass.class)
.assertSuccessWithOutput(EXPECTED);
}
-
- private static boolean willDesugarDefaultInterfaceMethods(AndroidApiLevel apiLevel) {
- return apiLevel != null && apiLevel.getLevel() < AndroidApiLevel.N.getLevel();
- }
}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/desugar/StaticInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/desugar/StaticInterfaceMethodTest.java
new file mode 100644
index 0000000..1dfd029
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/desugar/StaticInterfaceMethodTest.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2019, 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.naming.applymapping.desugar;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// Reproduction for b/196345511.
+@RunWith(Parameterized.class)
+public class StaticInterfaceMethodTest extends TestBase {
+
+ public static final String OUTPUT = "Called LibraryInterface::foo";
+ public static final String EXPECTED = StringUtils.lines(OUTPUT);
+
+ public interface LibraryInterface {
+ static void foo() {
+ System.out.println(OUTPUT);
+ }
+ }
+
+ public static class ProgramClass implements LibraryInterface {
+
+ public static void main(String[] args) {
+ LibraryInterface.foo();
+ }
+ }
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection params() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private final TestParameters parameters;
+
+ public StaticInterfaceMethodTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testJvm() throws Throwable {
+ Assume.assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addProgramClasses(LibraryInterface.class, ProgramClass.class)
+ .run(parameters.getRuntime(), ProgramClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testFullProgram() throws Throwable {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(LibraryInterface.class, ProgramClass.class)
+ .addKeepMainRule(ProgramClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), ProgramClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testLibraryLinkedWithProgram() throws Throwable {
+ R8TestCompileResult libraryResult =
+ testForR8(parameters.getBackend())
+ .addProgramClasses(LibraryInterface.class)
+ .addKeepClassAndMembersRules(LibraryInterface.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+ CodeInspector inspector = libraryResult.inspector();
+ ClassSubject libraryInterface = inspector.clazz(LibraryInterface.class);
+ assertThat(libraryInterface, isPresent());
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ assertThat(libraryInterface.method(LibraryInterface.class.getMethod("foo")), isPresent());
+ } else {
+ // Desugaring must remove the static on the interface.
+ assertThat(libraryInterface.method(LibraryInterface.class.getMethod("foo")), isAbsent());
+ // Check that we included the companion class and method.
+ ClassSubject companion =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(LibraryInterface.class));
+ assertThat(companion, isPresent());
+ assertEquals(1, companion.allMethods().size());
+ }
+
+ testForR8(parameters.getBackend())
+ .noTreeShaking()
+ .addProgramClasses(ProgramClass.class)
+ .addClasspathClasses(LibraryInterface.class)
+ .addApplyMapping(libraryResult.getProguardMap())
+ .addKeepMainRule(ProgramClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addRunClasspathFiles(libraryResult.writeToZip())
+ .run(parameters.getRuntime(), ProgramClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/DefaultMethodShadowedByStaticTest.java b/src/test/java/com/android/tools/r8/resolution/DefaultMethodShadowedByStaticTest.java
index 19424d8..b974111 100644
--- a/src/test/java/com/android/tools/r8/resolution/DefaultMethodShadowedByStaticTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/DefaultMethodShadowedByStaticTest.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.resolution.shadowing1.AClassDump;
import com.android.tools.r8.resolution.shadowing1.InterfaceDump;
@@ -39,22 +38,15 @@
@Test
public void testReference() throws Exception {
- TestRunResult<?> result =
- testForRuntime(parameters)
- .addProgramClassFileData(CLASSES)
- .run(parameters.getRuntime(), "Main");
- if (parameters.isDexRuntime()
- && (parameters.getApiLevel().isLessThan(apiLevelWithStaticInterfaceMethodsSupport())
- || parameters.getDexRuntimeVersion().equals(Version.V7_0_0))) {
- // TODO(b/167535447): Desugaring should preserve the error.
- result.assertSuccessWithOutputLines("42");
- } else if (parameters.isDexRuntime()
- && parameters.getDexRuntimeVersion().equals(Version.V7_0_0)) {
- // Note: VM 7.0.0 without desugaring of defaults will incorrectly allow the virtual dispatch.
- result.assertSuccessWithOutputLines("42");
- } else {
- result.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
- }
+ testForRuntime(parameters)
+ .addProgramClassFileData(CLASSES)
+ .run(parameters.getRuntime(), "Main")
+ .applyIf(
+ // When not desugaring interfaces, the v7 runtime fails to throw the correct error.
+ parameters.canUseDefaultAndStaticInterfaceMethods()
+ && parameters.isDexRuntimeVersion(Version.V7_0_0),
+ r -> r.assertSuccessWithOutputLines("42"),
+ r -> r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
index b176930..7d18aee 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
@@ -8,6 +8,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.AsmTestBase;
+import com.android.tools.r8.TestAppViewBuilder;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -89,12 +90,17 @@
@BeforeClass
public static void computeAppInfo() throws Exception {
appView =
- computeAppViewWithLiveness(
- buildClassesWithTestingAnnotations(CLASSES)
- .addClassProgramData(ASM_CLASSES)
- .addLibraryFile(getMostRecentAndroidJar())
- .build(),
- Main.class);
+ TestAppViewBuilder.builder()
+ .addAndroidApp(
+ buildClassesWithTestingAnnotations(CLASSES)
+ .addClassProgramData(ASM_CLASSES)
+ .addLibraryFile(getMostRecentAndroidJar())
+ .build())
+ .addKeepMainRule(Main.class)
+ // Some of these tests resolve default methods.
+ // If desugared they will hit the forward methods and not the expected defaults.
+ .setMinApi(apiLevelWithDefaultInterfaceMethodsSupport())
+ .buildWithLiveness();
appInfo = appView.appInfo();
}
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java
index cbad874..846c994 100644
--- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java
@@ -4,22 +4,18 @@
package com.android.tools.r8.resolution;
import static com.android.tools.r8.ToolHelper.getMostRecentAndroidJar;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import com.android.tools.r8.AsmTestBase;
-import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestRunResult;
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.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Assert;
@@ -150,22 +146,11 @@
@Test
public void runJvmAndD8() throws Exception {
- TestRunResult<?> runResult;
- if (parameters.isCfRuntime()) {
- runResult =
- testForJvm()
- .addProgramClasses(CLASSES)
- .addProgramClassFileData(DUMP)
- .run(parameters.getRuntime(), Main.class);
- } else {
- runResult =
- testForD8()
- .addProgramClasses(CLASSES)
- .addProgramClassFileData(DUMP)
- .setMinApi(parameters.getApiLevel())
- .run(parameters.getRuntime(), Main.class);
- }
- checkResult(runResult);
+ testForRuntime(parameters)
+ .addProgramClasses(CLASSES)
+ .addProgramClassFileData(DUMP)
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
}
@Test
@@ -179,33 +164,13 @@
}
public void runR8(boolean enableVerticalClassMerging) throws Exception {
- R8TestRunResult runResult =
- testForR8(parameters.getBackend())
- .addProgramClasses(CLASSES)
- .addProgramClassFileData(DUMP)
- .addKeepMainRule(Main.class)
- .setMinApi(parameters.getApiLevel())
- .addOptionsModification(o -> o.enableVerticalClassMerging = enableVerticalClassMerging)
- .run(parameters.getRuntime(), Main.class);
- if (enableVerticalClassMerging) {
- // Vertical class merging will merge B and C and change the instruction to invoke-virtual
- // causing the legacy ART runtime behavior to match the expected error.
- runResult.assertFailureWithErrorThatMatches(containsString("IncompatibleClassChangeError"));
- } else {
- checkResult(runResult);
- }
- }
-
- private void checkResult(TestRunResult<?> runResult) {
- runResult.assertFailureWithErrorThatMatches(containsString(expectedRuntimeError()));
- }
-
- private String expectedRuntimeError() {
- if (parameters.isDexRuntime()
- && parameters.getApiLevel().getLevel() < AndroidApiLevel.N.getLevel()) {
- // When desugaring default interface methods the error will be NoSuchMethodError.
- return "NoSuchMethodError";
- }
- return "IncompatibleClassChangeError";
+ testForR8(parameters.getBackend())
+ .addProgramClasses(CLASSES)
+ .addProgramClassFileData(DUMP)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(o -> o.enableVerticalClassMerging = enableVerticalClassMerging)
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
}
}
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java
index 4787b97..82c6b63 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java
@@ -10,6 +10,7 @@
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.TestAppViewBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRunResult;
@@ -20,6 +21,7 @@
import com.android.tools.r8.graph.MethodResolutionResult.NoSuchMethodResult;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -52,6 +54,7 @@
.withCfRuntimesStartingFromIncluding(JDK11)
.withDexRuntimes()
.withAllApiLevels()
+ .enableApiLevelsForCf()
.build(),
BooleanUtils.values(),
BooleanUtils.values());
@@ -132,7 +135,16 @@
MethodSubject foo = inspector.clazz(callerClass).uniqueMethodWithName("foo");
assertTrue(
foo.streamInstructions()
- .anyMatch(i -> i.asCfInstruction().isInvokeSpecial() && i.getMethod() == method));
+ .anyMatch(
+ i -> {
+ if (parameters.canUseDefaultAndStaticInterfaceMethodsWhenDesugaring()) {
+ return i.asCfInstruction().isInvokeSpecial() && i.getMethod() == method;
+ } else {
+ return i.isInvokeStatic()
+ && SyntheticItemsTestUtils.isInternalThrowNSME(
+ i.getMethod().asMethodReference());
+ }
+ }));
}
private DexMethod getTargetMethodSignature(Class<?> declaredClass, AppInfoWithLiveness appInfo) {
@@ -148,12 +160,13 @@
}
private AppView<AppInfoWithLiveness> getAppView() throws Exception {
- return computeAppViewWithLiveness(
- buildClasses(getClasses())
- .addClassProgramData(getTransformedClasses())
- .addLibraryFile(TestBase.runtimeJar(parameters.getBackend()))
- .build(),
- Main.class);
+ return TestAppViewBuilder.builder()
+ .addProgramClasses(getClasses())
+ .addProgramClassFileData(getTransformedClasses())
+ .addLibraryFiles(parameters.getDefaultRuntimeLibrary())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .buildWithLiveness();
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java
index 77fad9a..f6b2df5 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.TestAppViewBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -15,7 +16,6 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
-import java.util.Collections;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -42,12 +42,13 @@
// The resolution is runtime independent, so just run it on the default CF VM.
assumeTrue(parameters.useRuntimeAsNoneRuntime());
AppInfoWithLiveness appInfo =
- computeAppViewWithLiveness(
- buildClasses(CLASSES)
- .addClassProgramData(Collections.singletonList(transformB()))
- .addLibraryFile(parameters.getDefaultRuntimeLibrary())
- .build(),
- Main.class)
+ TestAppViewBuilder.builder()
+ .addProgramClasses(CLASSES)
+ .addProgramClassFileData(transformB())
+ .addLibraryFiles(parameters.getDefaultRuntimeLibrary())
+ .addKeepMainRule(Main.class)
+ .setMinApi(apiLevelWithDefaultInterfaceMethodsSupport())
+ .buildWithLiveness()
.appInfo();
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java
index 0139049..8901542 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java
@@ -5,17 +5,19 @@
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.TestAppViewBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -44,21 +46,45 @@
public void testResolution() throws Exception {
// The resolution is runtime independent, so just run it on the default CF VM.
assumeTrue(parameters.useRuntimeAsNoneRuntime());
- AppInfoWithLiveness appInfo =
- computeAppViewWithLiveness(
- buildClasses(CLASSES)
- .addClassProgramData(Collections.singletonList(transformB()))
- .addLibraryFile(parameters.getDefaultRuntimeLibrary())
- .build(),
- Main.class)
- .appInfo();
- DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
- MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
- Set<String> holders = new HashSet<>();
- resolutionResult
- .asFailedResolution()
- .forEachFailureDependency(m -> holders.add(m.getHolderType().toSourceString()));
- assertEquals(ImmutableSet.of(I.class.getTypeName(), J.class.getTypeName()), holders);
+ for (AndroidApiLevel minApi :
+ ImmutableList.of(AndroidApiLevel.B, apiLevelWithDefaultInterfaceMethodsSupport())) {
+ AppInfoWithLiveness appInfo =
+ TestAppViewBuilder.builder()
+ .addProgramClasses(CLASSES)
+ .addProgramClassFileData(transformB())
+ .addLibraryFiles(parameters.getDefaultRuntimeLibrary())
+ .addKeepMainRule(Main.class)
+ .setMinApi(minApi)
+ .buildWithLiveness()
+ .appInfo();
+ DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ if (minApi.isLessThan(apiLevelWithDefaultInterfaceMethodsSupport())) {
+ // When desugaring a forwarding method throwing ICCE is inserted.
+ // Check that the resolved method throws such an exception.
+ assertTrue(
+ resolutionResult
+ .asSingleResolution()
+ .getResolvedMethod()
+ .getCode()
+ .asCfCode()
+ .getInstructions()
+ .stream()
+ .anyMatch(
+ i ->
+ i.isTypeInstruction()
+ && i.asTypeInstruction().getType()
+ == appInfo.dexItemFactory().icceType));
+ } else {
+ // When not desugaring resolution should fail. Check the failure dependencies are the two
+ // default methods in conflict.
+ Set<String> holders = new HashSet<>();
+ resolutionResult
+ .asFailedResolution()
+ .forEachFailureDependency(m -> holders.add(m.getHolderType().toSourceString()));
+ assertEquals(ImmutableSet.of(I.class.getTypeName(), J.class.getTypeName()), holders);
+ }
+ }
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
index 0ec1ce8..4dcead3 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.resolution.interfacetargets;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assume.assumeTrue;
import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
@@ -17,7 +16,6 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DescriptorUtils;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
@@ -70,7 +68,7 @@
.addProgramClasses(A.class, I.class)
.addProgramClassFileData(transformMain())
.run(parameters.getRuntime(), Main.class)
- .assertFailureWithErrorThatMatches(containsString(getExpected()));
+ .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
}
@Test
@@ -81,14 +79,7 @@
.addKeepMainRule(Main.class)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertFailureWithErrorThatMatches(containsString(getExpected()));
- }
-
- private String getExpected() {
- return parameters.isCfRuntime()
- || parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)
- ? "IncompatibleClassChangeError"
- : "NoSuchMethodError";
+ .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
}
private byte[] transformMain() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java b/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
index 4a8306a..801d678 100644
--- a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
@@ -4,9 +4,9 @@
package com.android.tools.r8.shaking;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
-import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -105,9 +105,9 @@
"In C.m3()",
"In A.m4()",
"In A.m1()", // With Java: Caught IllegalAccessError when calling B.m1()
- "In A.m3()", // With Java: Caught IncompatibleClassChangeError when calling B.m3()
+ "Caught IncompatibleClassChangeError when calling B.m3()",
"In C.m1()", // With Java: Caught IllegalAccessError when calling B.m1()
- "In C.m3()", // With Java: Caught IncompatibleClassChangeError when calling B.m3()
+ "Caught IncompatibleClassChangeError when calling B.m3()",
"In C.m1()",
"In C.m3()",
"");
@@ -178,9 +178,11 @@
ClassSubject classSubject = inspector.clazz(B.class.getName());
assertThat(classSubject, isPresentAndRenamed());
assertThat(classSubject.method("void", "m1", ImmutableList.of()), isPresent());
- assertThat(classSubject.method("void", "m2", ImmutableList.of()), not(isPresent()));
- assertThat(classSubject.method("void", "m3", ImmutableList.of()), isPresent());
- assertThat(classSubject.method("void", "m4", ImmutableList.of()), not(isPresent()));
+ assertThat(classSubject.method("void", "m2", ImmutableList.of()), isAbsent());
+ assertThat(
+ classSubject.method("void", "m3", ImmutableList.of()),
+ parameters.isCfRuntime() ? isPresent() : isAbsent());
+ assertThat(classSubject.method("void", "m4", ImmutableList.of()), isAbsent());
}
}
@@ -263,7 +265,8 @@
System.out.println("In B.m2()");
}
- // Made static in the dump below. This method is targeted and can therefore not be removed.
+ // Made static in the dump below. Ends up dead as the targeting call is replaced by throw ICCE.
+ // Except in non-desugaring CF the method will remain instead of inserting a stub.
@Override
public void m3() {
System.out.println("In B.m3()");
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java
index b427736..29960f9 100644
--- a/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java
@@ -4,8 +4,8 @@
package com.android.tools.r8.shaking.clinit;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -51,22 +51,27 @@
.compile()
.inspect(
inspector -> {
- // Verify that I's class initializer is still present.
ClassSubject iClassSubject = inspector.clazz(I.class);
- assertThat(iClassSubject, isPresent());
- assertThat(
- iClassSubject.clinit(),
- onlyIf(hasDefaultInterfaceMethodsSupport(parameters), isPresent()));
-
- // Verify that J is still there.
ClassSubject jClassSubject = inspector.clazz(J.class);
- assertThat(jClassSubject, isPresent());
-
- // Verify that A still implements J.
ClassSubject aClassSubject = inspector.clazz(A.class);
- assertThat(aClassSubject, isPresent());
- assertEquals(1, aClassSubject.getDexProgramClass().getInterfaces().size());
- assertTrue(aClassSubject.isImplementing(jClassSubject));
+ if (hasDefaultInterfaceMethodsSupport(parameters)) {
+ // Verify that I's class initializer is still present.
+ assertThat(iClassSubject, isPresent());
+ assertThat(iClassSubject.clinit(), isPresent());
+
+ // Verify that J is still there.
+ assertThat(jClassSubject, isPresent());
+
+ // Verify that A still implements J.
+ assertThat(aClassSubject, isPresent());
+ assertEquals(1, aClassSubject.getDexProgramClass().getInterfaces().size());
+ assertTrue(aClassSubject.isImplementing(jClassSubject));
+ } else {
+ // All interfaces are gone and the default methods companion call is inlined.
+ assertThat(iClassSubject, isAbsent());
+ assertThat(jClassSubject, isAbsent());
+ assertThat(aClassSubject, isAbsent());
+ }
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLinesIf(
diff --git a/src/test/java/com/android/tools/r8/shaking/desugar/KeepRuleWarningTest.java b/src/test/java/com/android/tools/r8/shaking/desugar/KeepRuleWarningTest.java
index c2be7a8..02dfda4 100644
--- a/src/test/java/com/android/tools/r8/shaking/desugar/KeepRuleWarningTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/desugar/KeepRuleWarningTest.java
@@ -3,8 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking.desugar;
-import static org.hamcrest.CoreMatchers.containsString;
-
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessages;
@@ -106,14 +104,8 @@
.enableNoVerticalClassMergingAnnotations()
.addKeepMainRule(MAIN)
.addKeepRules("-keep interface **.I { static void foo(); }")
- .allowDiagnosticWarningMessages()
.compile()
- .inspectDiagnosticMessages(
- m ->
- m.assertWarningsCount(1)
- .assertWarningMessageThatMatches(containsString("static void foo()"))
- .assertWarningMessageThatMatches(containsString("is ignored"))
- .assertWarningMessageThatMatches(containsString("will be desugared")))
+ .inspectDiagnosticMessages(TestDiagnosticMessages::assertNoMessages)
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/interfacemethoddesugaring/IfRuleWithInterfaceMethodDesugaringTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/interfacemethoddesugaring/IfRuleWithInterfaceMethodDesugaringTest.java
index 19b87e4..01d977a 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/interfacemethoddesugaring/IfRuleWithInterfaceMethodDesugaringTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/interfacemethoddesugaring/IfRuleWithInterfaceMethodDesugaringTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.shaking.ifrule.interfacemethoddesugaring;
-import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringForTesting.getCompanionClassNameSuffix;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPublic;
import static com.android.tools.r8.utils.codeinspector.Matchers.isStatic;
@@ -12,6 +11,8 @@
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
@@ -34,9 +35,17 @@
private final TestParameters parameters;
+ private static final String STATIC_STR = "In Interface.staticMethod()";
+ private static final String VIRTUAL_STR = "In Interface.virtualMethod()";
+ private static final String EXPECTED_OUTPUT = StringUtils.lines(STATIC_STR, VIRTUAL_STR);
+
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withDexRuntimes().withApiLevel(AndroidApiLevel.M).build();
+ return getTestParameters()
+ .withCfRuntimes()
+ .withDexRuntimes()
+ .withApiLevel(AndroidApiLevel.M)
+ .build();
}
public IfRuleWithInterfaceMethodDesugaringTest(TestParameters parameters) {
@@ -44,50 +53,64 @@
}
@Test
+ public void testReference() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addTestClasspath()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Test
public void test() throws Exception {
- String expectedOutput =
- StringUtils.lines("In Interface.staticMethod()", "In Interface.virtualMethod()");
-
- testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expectedOutput);
-
CodeInspector inspector =
testForR8(parameters.getBackend())
.addInnerClasses(IfRuleWithInterfaceMethodDesugaringTest.class)
.addKeepMainRule(TestClass.class)
.addKeepRules(
"-if class " + Interface.class.getTypeName() + " {",
- " !public static void staticMethod();",
+ " static void staticMethod();",
"}",
"-keep class " + Unused1.class.getTypeName(),
"-if class " + Interface.class.getTypeName() + " {",
- " !public !static void virtualMethod();",
+ " !static void virtualMethod();",
"}",
"-keep class " + Unused2.class.getTypeName())
- .allowUnusedProguardConfigurationRules()
+ .allowUnusedProguardConfigurationRules(parameters.isDexRuntime())
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
- .compile()
.run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(expectedOutput)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT)
.inspector();
- ClassSubject classSubject =
- inspector.clazz(Interface.class.getTypeName() + getCompanionClassNameSuffix());
- assertThat(classSubject, isPresent());
- assertEquals(2, classSubject.allMethods().size());
+ if (parameters.isCfRuntime()) {
+ ClassSubject itfClass = inspector.clazz(Interface.class.getTypeName());
+ assertThat(itfClass, isPresent());
+ assertThat(itfClass.uniqueMethodWithName("staticMethod"), isPresent());
+ assertThat(itfClass.uniqueMethodWithName("virtualMethod"), isPresent());
+ assertThat(inspector.clazz(Unused1.class), isPresent());
+ assertThat(inspector.clazz(Unused2.class), isPresent());
+ return;
+ }
+ ClassSubject classSubject = inspector.clazz(Interface.class.getTypeName()).toCompanionClass();
+ assertThat(classSubject, isPresent());
+
+ // NeverInline is only applicable to the static method at this point (could change).
+ assertEquals(1, classSubject.allMethods().size());
MethodSubject staticMethodSubject = classSubject.uniqueMethodWithName("staticMethod");
assertThat(staticMethodSubject, allOf(isPresent(), isPublic(), isStatic()));
+ assertTrue(staticMethodSubject.streamInstructions().anyMatch(i -> i.isConstString(STATIC_STR)));
- // TODO(b/120764902): MethodSubject.getOriginalName() not working in presence of desugaring.
- MethodSubject virtualMethodSubject =
- classSubject.allMethods().stream()
- .filter(subject -> subject != staticMethodSubject)
- .findFirst()
- .get();
- assertThat(virtualMethodSubject, allOf(isPresent(), isPublic(), isStatic()));
+ // The virtual method is inlined as @NeverInline does not apply at this point (could change).
+ assertTrue(
+ inspector
+ .clazz(TestClass.class)
+ .mainMethod()
+ .streamInstructions()
+ .anyMatch(i -> i.isConstString(VIRTUAL_STR)));
// TODO(b/122875545): The Unused class should be present due to the -if rule.
assertThat(inspector.clazz(Unused1.class), not(isPresent()));
diff --git a/src/test/java/com/android/tools/r8/shaking/staticinterfacemethods/defaultmethods/StaticInterfaceMethodsTest.java b/src/test/java/com/android/tools/r8/shaking/staticinterfacemethods/defaultmethods/StaticInterfaceMethodsTest.java
index ca0aaa5..e9af866 100644
--- a/src/test/java/com/android/tools/r8/shaking/staticinterfacemethods/defaultmethods/StaticInterfaceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/staticinterfacemethods/defaultmethods/StaticInterfaceMethodsTest.java
@@ -107,7 +107,7 @@
assertFalse(clazz.method("int", "method", ImmutableList.of()).isPresent());
}
- private void staticMethodKeptB159987443(CodeInspector inspector) {
+ private void staticMethodKept(CodeInspector inspector) {
ClassSubject clazz = inspector.clazz(InterfaceWithStaticMethods.class);
assertThat(clazz, isPresent());
MethodSubject method = clazz.method("int", "method", ImmutableList.of());
@@ -117,13 +117,12 @@
assertThat(companionClass, not(isPresent()));
} else {
assertThat(method, not(isPresent()));
- // TODO(159987443): The companion class should be present.
- assertThat(companionClass, not(isPresent()));
- // Also check that method exists on companion class.
+ assertThat(companionClass, isPresent());
+ assertThat(companionClass.uniqueMethodWithName("method"), isPresent());
}
}
- private void staticMethodKept(CodeInspector inspector) {
+ private void staticMethodKeptB160142903(CodeInspector inspector) {
ClassSubject clazz = inspector.clazz(InterfaceWithStaticMethods.class);
ClassSubject companionClass = clazz.toCompanionClass();
MethodSubject method = clazz.method("int", "method", ImmutableList.of());
@@ -136,7 +135,7 @@
// after desugaring, only the companion class is left.
assertThat(clazz, not(isPresent()));
assertThat(method, not(isPresent()));
- // TODO(159987443): The companion class should be present.
+ // TODO(160142903): The companion class should be present.
assertThat(companionClass, not(isPresent()));
// Also check that method exists on companion class.
}
@@ -168,7 +167,7 @@
"-keep interface " + InterfaceWithStaticMethods.class.getTypeName() + "{",
" <methods>;",
"}"),
- this::staticMethodKeptB159987443);
+ this::staticMethodKept);
}
@Test
@@ -196,7 +195,7 @@
"}");
}
}
- runTest(builder.build(), this::staticMethodKept);
+ runTest(builder.build(), this::staticMethodKeptB160142903);
}
public static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index 6ffbce9..92774e1 100644
--- a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -21,9 +21,12 @@
}
public static ClassReference syntheticCompanionClass(Class<?> clazz) {
+ return syntheticCompanionClass(Reference.classFromClass(clazz));
+ }
+
+ public static ClassReference syntheticCompanionClass(ClassReference clazz) {
return Reference.classFromDescriptor(
- InterfaceDesugaringForTesting.getCompanionClassDescriptor(
- Reference.classFromClass(clazz).getDescriptor()));
+ InterfaceDesugaringForTesting.getCompanionClassDescriptor(clazz.getDescriptor()));
}
private static ClassReference syntheticClass(Class<?> clazz, SyntheticKind kind, int id) {
@@ -72,7 +75,8 @@
public static boolean isExternalSynthetic(ClassReference reference) {
for (SyntheticKind kind : SyntheticKind.values()) {
- if (kind == SyntheticKind.RECORD_TAG) {
+ if (kind == SyntheticKind.RECORD_TAG
+ || kind == SyntheticKind.EMULATED_INTERFACE_MARKER_CLASS) {
continue;
}
if (kind.isFixedSuffixSynthetic) {
@@ -134,4 +138,9 @@
public static Matcher<String> containsExternalSyntheticReference() {
return containsString(SyntheticNaming.getPhaseSeparator(Phase.EXTERNAL));
}
+
+ public static boolean isInternalThrowNSME(MethodReference method) {
+ return SyntheticNaming.isSynthetic(
+ method.getHolderClass(), Phase.INTERNAL, SyntheticKind.THROW_NSME);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index f552a25..b100332 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.utils.codeinspector;
-import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringForTesting.getCompanionClassNameSuffix;
-
import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -13,11 +11,11 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
-import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.retrace.RetraceClassElement;
import com.android.tools.r8.retrace.RetraceClassResult;
import com.android.tools.r8.smali.SmaliBuilder;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.ListUtils;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
@@ -231,12 +229,7 @@
public abstract KotlinClassMetadata getKotlinClassMetadata();
public ClassSubject toCompanionClass() {
- String descriptor = reference.getDescriptor();
- return codeInspector.clazz(
- Reference.classFromDescriptor(
- descriptor.substring(0, descriptor.length() - 1)
- + getCompanionClassNameSuffix()
- + ";"));
+ return codeInspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(reference));
}
public abstract RetraceClassResult retrace();
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 77c68b3..76d76e5 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -3,8 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils.codeinspector;
-import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringForTesting.getCompanionClassNameSuffix;
-
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.StringResource;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
@@ -40,6 +38,7 @@
import com.android.tools.r8.retrace.Retracer;
import com.android.tools.r8.retrace.internal.DirectClassNameMapperProguardMapProducer;
import com.android.tools.r8.retrace.internal.RetracerImpl;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.BiMapContainer;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -328,7 +327,7 @@
}
public ClassSubject companionClassFor(Class<?> clazz) {
- return clazz(Reference.classFromTypeName(clazz.getTypeName() + getCompanionClassNameSuffix()));
+ return clazz(SyntheticItemsTestUtils.syntheticCompanionClass(clazz));
}
public void forAllClasses(Consumer<FoundClassSubject> inspection) {