Insert bridges for kept interface methods after enqueueing.
Change-Id: Ide12b2b77a52fdd64b51a2a9fcd7306b5d6b2e7a
diff --git a/src/main/java/com/android/tools/r8/shaking/DelayedRootSetActionItem.java b/src/main/java/com/android/tools/r8/shaking/DelayedRootSetActionItem.java
index 773db00..1c99cc6 100644
--- a/src/main/java/com/android/tools/r8/shaking/DelayedRootSetActionItem.java
+++ b/src/main/java/com/android/tools/r8/shaking/DelayedRootSetActionItem.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.shaking;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.ProgramMethod;
import java.util.function.Consumer;
public abstract class DelayedRootSetActionItem {
@@ -18,24 +18,22 @@
}
public static class InterfaceMethodSyntheticBridgeAction extends DelayedRootSetActionItem {
- private final DexEncodedMethod methodToKeep;
- private final DexEncodedMethod singleTarget;
+ private final ProgramMethod methodToKeep;
+ private final ProgramMethod singleTarget;
private final Consumer<RootSetBuilder> action;
InterfaceMethodSyntheticBridgeAction(
- DexEncodedMethod methodToKeep,
- DexEncodedMethod singleTarget,
- Consumer<RootSetBuilder> action) {
+ ProgramMethod methodToKeep, ProgramMethod singleTarget, Consumer<RootSetBuilder> action) {
this.methodToKeep = methodToKeep;
this.singleTarget = singleTarget;
this.action = action;
}
- public DexEncodedMethod getMethodToKeep() {
+ public ProgramMethod getMethodToKeep() {
return methodToKeep;
}
- public DexEncodedMethod getSingleTarget() {
+ public ProgramMethod getSingleTarget() {
return singleTarget;
}
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 ac1dea1..1b76214 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -2263,6 +2263,13 @@
(field, info) -> field != info.getField() || info == MISSING_FIELD_ACCESS_INFO);
assert fieldAccessInfoCollection.verifyMappingIsOneToOne();
+ for (ProgramMethod bridge : syntheticInterfaceMethodBridges.values()) {
+ appView.appInfo().invalidateTypeCacheFor(bridge.holder.type);
+ bridge.holder.appendVirtualMethod(bridge.method);
+ targetedMethods.add(bridge.method, graphReporter.fakeReportShouldNotBeUsed());
+ liveMethods.add(bridge.holder, bridge.method, graphReporter.fakeReportShouldNotBeUsed());
+ }
+
AppInfoWithLiveness appInfoWithLiveness =
new AppInfoWithLiveness(
appInfo,
@@ -2460,25 +2467,31 @@
return builder.buildConsequentRootSet();
}
+ private Map<DexMethod, ProgramMethod> syntheticInterfaceMethodBridges = new IdentityHashMap<>();
+
private void handleInterfaceMethodSyntheticBridgeAction(
InterfaceMethodSyntheticBridgeAction action, RootSetBuilder builder) {
- if (rootSet.noShrinking.containsKey(action.getSingleTarget().method)) {
+ ProgramMethod methodToKeep = action.getMethodToKeep();
+ ProgramMethod singleTarget = action.getSingleTarget();
+ if (rootSet.noShrinking.containsKey(singleTarget.method.method)) {
return;
}
- DexEncodedMethod methodToKeep = action.getMethodToKeep();
- DexEncodedMethod singleTarget = action.getSingleTarget();
- DexClass clazz = getProgramClassOrNull(methodToKeep.method.holder);
if (methodToKeep != singleTarget) {
- // Insert a bridge method.
- if (appView.definitionFor(methodToKeep.method) == null) {
- clazz.appendVirtualMethod(methodToKeep);
- if (singleTarget.isLibraryMethodOverride().isTrue()) {
- methodToKeep.setLibraryMethodOverride(OptionalBool.TRUE);
+ assert null == methodToKeep.holder.lookupMethod(methodToKeep.method.method);
+ ProgramMethod old =
+ syntheticInterfaceMethodBridges.put(methodToKeep.method.method, methodToKeep);
+ if (old == null) {
+ if (singleTarget.method.isLibraryMethodOverride().isTrue()) {
+ methodToKeep.method.setLibraryMethodOverride(OptionalBool.TRUE);
}
- appView.appInfo().invalidateTypeCacheFor(methodToKeep.method.holder);
- // The addition of a bridge method can lead to a change of resolution, thus the cached
- // resolution targets are invalid.
- virtualTargetsMarkedAsReachable.remove(methodToKeep.method);
+ assert singleTarget.holder.isInterface();
+ markVirtualMethodAsReachable(
+ singleTarget.method.method,
+ singleTarget.holder.isInterface(),
+ null,
+ graphReporter.fakeReportShouldNotBeUsed());
+ enqueueMarkMethodLiveAction(
+ singleTarget.holder, singleTarget.method, graphReporter.fakeReportShouldNotBeUsed());
}
}
action.getAction().accept(builder);
diff --git a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
index cf820bc..c172b52 100644
--- a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
+++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -67,6 +67,10 @@
}
}
+ public KeepReasonWitness fakeReportShouldNotBeUsed() {
+ return KeepReasonWitness.INSTANCE;
+ }
+
public boolean verifyRootedPath(DexProgramClass liveType) {
assert verificationGraphConsumer != null;
ClassGraphNode node = getClassGraphNode(liveType.type);
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index b842680..8555b93 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -23,7 +23,8 @@
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DirectMappedDexApplication;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteBuilderShrinker;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.shaking.DelayedRootSetActionItem.InterfaceMethodSyntheticBridgeAction;
@@ -457,7 +458,9 @@
}
// TODO(b/143643942): Generalize the below approach to also work for subtyping hierarchies in
// fullmode.
- if (clazz.isProgramClass()) {
+ if (clazz.isProgramClass()
+ && rule.isProguardKeepRule()
+ && !rule.asProguardKeepRule().getModifiers().allowsShrinking) {
new SynthesizeMissingInterfaceMethodsForMemberRules(
clazz.asProgramClass(), memberKeepRules, rule, preconditionSupplier, ifRule)
.run();
@@ -484,6 +487,8 @@
ProguardConfigurationRule context,
Map<Predicate<DexDefinition>, DexDefinition> preconditionSupplier,
ProguardIfRule ifRule) {
+ assert context.isProguardKeepRule();
+ assert !context.asProguardKeepRule().getModifiers().allowsShrinking;
this.originalClazz = originalClazz;
this.memberKeepRules = memberKeepRules;
this.context = context;
@@ -525,49 +530,46 @@
}
private void tryAndKeepMethodOnClass(DexEncodedMethod method, ProguardMemberRule rule) {
- boolean shouldKeepMethod =
- context.isProguardKeepRule()
- && !context.asProguardKeepRule().getModifiers().allowsShrinking;
- if (!shouldKeepMethod) {
+ SingleResolutionResult resolutionResult =
+ appView.appInfo().resolveMethod(originalClazz, method.method).asSingleResolution();
+ if (resolutionResult == null || !resolutionResult.isVirtualTarget()) {
return;
}
- ResolutionResult resolutionResult =
- appView.appInfo().resolveMethod(originalClazz, method.method);
- if (!resolutionResult.isVirtualTarget() || !resolutionResult.isSingleResolution()) {
+ if (resolutionResult.getResolvedHolder() == originalClazz
+ || resolutionResult.getResolvedHolder().isNotProgramClass()) {
return;
}
- DexEncodedMethod methodToKeep = resolutionResult.getSingleTarget();
- if (methodToKeep.method.holder == originalClazz.type) {
- return;
- }
- DexClass holder = appView.definitionFor(methodToKeep.method.holder);
- if (holder.isNotProgramClass()) {
- return;
- }
- if (!holder.isInterface()) {
+ if (!resolutionResult.getResolvedHolder().isInterface()) {
// TODO(b/143643942): For fullmode, this check should probably be removed.
return;
}
- if (canInsertForwardingMethod(originalClazz, methodToKeep)) {
- methodToKeep = methodToKeep.toForwardingMethod(originalClazz, appView);
- }
- final DexEncodedMethod finalKeepMethod = methodToKeep;
+ ProgramMethod resolutionMethod =
+ new ProgramMethod(
+ resolutionResult.getResolvedHolder().asProgramClass(),
+ resolutionResult.getResolvedMethod());
+ ProgramMethod methodToKeep =
+ canInsertForwardingMethod(originalClazz, resolutionMethod.method)
+ ? new ProgramMethod(
+ originalClazz, resolutionMethod.method.toForwardingMethod(originalClazz, appView))
+ : resolutionMethod;
+
delayedRootSetActionItems.add(
new InterfaceMethodSyntheticBridgeAction(
methodToKeep,
- resolutionResult.getSingleTarget(),
+ resolutionMethod,
(rootSetBuilder) -> {
if (Log.ENABLED) {
Log.verbose(
getClass(),
"Marking method `%s` due to `%s { %s }`.",
- finalKeepMethod,
+ methodToKeep,
context,
rule);
}
DexDefinition precondition =
- testAndGetPrecondition(finalKeepMethod, preconditionSupplier);
- rootSetBuilder.addItemToSets(finalKeepMethod, context, rule, precondition, ifRule);
+ testAndGetPrecondition(methodToKeep.method, preconditionSupplier);
+ rootSetBuilder.addItemToSets(
+ methodToKeep.method, context, rule, precondition, ifRule);
}));
}
}