Maintain canonical interface bridges.
Bug: b/228791247
Change-Id: I81e2d06133c15a57c0547c66313cc7fdddafe1cc
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 b925309..6a72328 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -85,6 +85,7 @@
import java.util.Set;
import java.util.Stack;
import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -128,6 +129,7 @@
private final Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects = new IdentityHashMap<>();
private final Map<DexMember<?, ?>, ProguardMemberRule> assumedValues = new IdentityHashMap<>();
private final Set<DexReference> identifierNameStrings = Sets.newIdentityHashSet();
+ private final Map<DexMethod, ProgramMethod> keptMethodBridges = new ConcurrentHashMap<>();
private final Queue<DelayedRootSetActionItem> delayedRootSetActionItems =
new ConcurrentLinkedQueue<>();
private final InternalOptions options;
@@ -627,16 +629,24 @@
// TODO(b/143643942): For fullmode, this check should probably be removed.
return;
}
- ProgramMethod resolutionMethod =
- new ProgramMethod(
- resolutionResult.getResolvedHolder().asProgramClass(),
- resolutionResult.getResolvedMethod());
- ProgramMethod methodToKeep =
- canInsertForwardingMethod(originalClazz, resolutionMethod.getDefinition())
- ? new ProgramMethod(
- originalClazz,
- resolutionMethod.getDefinition().toForwardingMethod(originalClazz, appView))
- : resolutionMethod;
+ ProgramMethod resolutionMethod = resolutionResult.getResolvedProgramMethod();
+ ProgramMethod methodToKeep;
+ if (canInsertForwardingMethod(originalClazz, resolutionMethod.getDefinition())) {
+ DexMethod methodToKeepReference =
+ resolutionMethod.getReference().withHolder(originalClazz, appView.dexItemFactory());
+ methodToKeep =
+ keptMethodBridges.computeIfAbsent(
+ methodToKeepReference,
+ k ->
+ new ProgramMethod(
+ originalClazz,
+ resolutionMethod
+ .getDefinition()
+ .toForwardingMethod(originalClazz, appView)));
+ assert methodToKeepReference.equals(methodToKeep.getReference());
+ } else {
+ methodToKeep = resolutionMethod;
+ }
delayedRootSetActionItems.add(
new InterfaceMethodSyntheticBridgeAction(
diff --git a/src/test/java/com/android/tools/r8/shaking/interfacebridge/MultipleRulesRegression228791247Test.java b/src/test/java/com/android/tools/r8/shaking/methods/interfaces/MultipleRulesRegression228791247Test.java
similarity index 70%
rename from src/test/java/com/android/tools/r8/shaking/interfacebridge/MultipleRulesRegression228791247Test.java
rename to src/test/java/com/android/tools/r8/shaking/methods/interfaces/MultipleRulesRegression228791247Test.java
index d3d9804..c61fa16 100644
--- a/src/test/java/com/android/tools/r8/shaking/interfacebridge/MultipleRulesRegression228791247Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/methods/interfaces/MultipleRulesRegression228791247Test.java
@@ -1,12 +1,10 @@
// Copyright (c) 2022, 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.shaking.interfacebridge;
+package com.android.tools.r8.shaking.methods.interfaces;
import static com.android.tools.r8.references.Reference.classFromClass;
-import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -44,22 +42,13 @@
// Regression adds two rules causes the forwarding method to be generated twice.
String rule1 = "-keep class " + classFromClass(J.class).getTypeName() + "{ void *oo(); }";
String rule2 = "-keep class " + classFromClass(J.class).getTypeName() + "{ void fo*(); }";
- try {
- testForR8(parameters.getBackend())
- .addProgramClasses(I.class, J.class, A.class, TestClass.class)
- .addKeepMainRule(TestClass.class)
- .addKeepRules(rule1, rule2)
- .setMinApi(parameters.getApiLevel())
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED);
- assertTrue(
- parameters.isCfRuntime()
- || parameters
- .getApiLevel()
- .isGreaterThanOrEqualTo(apiLevelWithDefaultInterfaceMethodsSupport()));
- } catch (CompilationFailedException e) {
- assertTrue(parameters.getApiLevel().isLessThan(apiLevelWithDefaultInterfaceMethodsSupport()));
- }
+ testForR8(parameters.getBackend())
+ .addProgramClasses(I.class, J.class, A.class, TestClass.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepRules(rule1, rule2)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
}
public interface I {