Remove collision detection from vertical class merging
Change-Id: Id844c246af9417cd2ccfa90bee40f31b41cffd94
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 6923215..12a84ab 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -34,7 +34,6 @@
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.graph.analysis.ClassInitializerAssertionEnablingAnalysis;
import com.android.tools.r8.graph.analysis.InitializedClassesInInstanceMethodsAnalysis;
-import com.android.tools.r8.graph.lens.AppliedGraphLens;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.conversion.IRConverter;
@@ -483,7 +482,9 @@
// should therefore be run after the publicizer.
new NestReducer(appViewWithLiveness).run(executorService, timing);
- new MemberRebindingAnalysis(appViewWithLiveness).run(executorService);
+ appView.setGraphLens(MemberRebindingIdentityLensFactory.create(appView, executorService));
+
+ new MemberRebindingAnalysis(appViewWithLiveness).run();
appViewWithLiveness.appInfo().notifyMemberRebindingFinished(appViewWithLiveness);
assert ArtProfileCompletenessChecker.verify(appView);
@@ -540,9 +541,7 @@
// At this point all code has been mapped according to the graph lens. We cannot remove the
// graph lens entirely, though, since it is needed for mapping all field and method signatures
// back to the original program.
- timing.time(
- "AppliedGraphLens construction",
- () -> appView.setGraphLens(new AppliedGraphLens(appView)));
+ timing.time("AppliedGraphLens construction", appView::flattenGraphLenses);
timing.end();
RuntimeTypeCheckInfo.Builder finalRuntimeTypeCheckInfoBuilder = null;
diff --git a/src/main/java/com/android/tools/r8/classmerging/ClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/classmerging/ClassMergerGraphLens.java
index 724079b..300d399 100644
--- a/src/main/java/com/android/tools/r8/classmerging/ClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/classmerging/ClassMergerGraphLens.java
@@ -31,7 +31,7 @@
GL extends ClassMergerGraphLens, MC extends MergedClasses> {
public abstract void addExtraParameters(
- DexMethod methodSignature, List<? extends ExtraParameter> extraParameters);
+ DexMethod from, DexMethod to, List<? extends ExtraParameter> extraParameters);
public abstract void commitPendingUpdates();
diff --git a/src/main/java/com/android/tools/r8/classmerging/ClassMergerTreeFixer.java b/src/main/java/com/android/tools/r8/classmerging/ClassMergerTreeFixer.java
index 08933ed..5e0445e 100644
--- a/src/main/java/com/android/tools/r8/classmerging/ClassMergerTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/classmerging/ClassMergerTreeFixer.java
@@ -19,7 +19,6 @@
import com.android.tools.r8.graph.classmerging.MergedClasses;
import com.android.tools.r8.graph.fixup.TreeFixerBase;
import com.android.tools.r8.horizontalclassmerging.SubtypingForrestForClasses;
-import com.android.tools.r8.horizontalclassmerging.SyntheticArgumentClass;
import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
import com.android.tools.r8.shaking.AnnotationFixer;
@@ -27,13 +26,14 @@
import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.OptionalBool;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
+import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
+import com.android.tools.r8.utils.collections.DexMethodSignatureBiMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.util.Collection;
import java.util.IdentityHashMap;
-import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -52,8 +52,8 @@
private final SyntheticArgumentClass syntheticArgumentClass;
private final Map<DexProgramClass, DexType> originalSuperTypes = new IdentityHashMap<>();
- private final BiMap<DexMethodSignature, DexMethodSignature> reservedInterfaceSignatures =
- HashBiMap.create();
+ private final DexMethodSignatureBiMap<DexMethodSignature> reservedInterfaceSignatures =
+ new DexMethodSignatureBiMap<>();
public ClassMergerTreeFixer(
AppView<?> appView,
@@ -68,10 +68,11 @@
this.syntheticArgumentClass = syntheticArgumentClass;
}
- public GL run(ExecutorService executorService) throws ExecutionException {
+ public GL run(ExecutorService executorService, Timing timing) throws ExecutionException {
if (!appView.enableWholeProgramOptimizations()) {
- return lensBuilder.build(appView, mergedClasses);
+ return timing.time("Fixup", () -> lensBuilder.build(appView, mergedClasses));
}
+ timing.begin("Fixup");
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
Collection<DexProgramClass> classes = appView.appInfo().classesWithDeterministicOrder();
Iterables.filter(classes, DexProgramClass::isInterface).forEach(this::fixupInterfaceClass);
@@ -81,15 +82,22 @@
new SubtypingForrestForClasses(appView.withClassHierarchy());
// TODO(b/170078037): parallelize this code segment.
for (DexProgramClass root : subtypingForrest.getProgramRoots()) {
- subtypingForrest.traverseNodeDepthFirst(root, HashBiMap.create(), this::fixupProgramClass);
+ subtypingForrest.traverseNodeDepthFirst(
+ root, new DexMethodSignatureBiMap<>(), this::fixupProgramClass);
}
+ postprocess();
GL lens = lensBuilder.build(appViewWithLiveness, mergedClasses);
new AnnotationFixer(appView, lens).run(appView.appInfo().classes(), executorService);
+ timing.end();
return lens;
}
public abstract boolean isRunningBeforePrimaryOptimizationPass();
+ public void postprocess() {
+ // Intentionally empty.
+ }
+
public void fixupAttributes(DexProgramClass clazz) {
if (clazz.hasEnclosingMethodAttribute()) {
EnclosingMethodAttribute enclosingMethodAttribute = clazz.getEnclosingMethodAttribute();
@@ -115,24 +123,25 @@
clazz.setInterfaces(fixupInterfaces(clazz, clazz.getInterfaces()));
}
- private BiMap<DexMethodSignature, DexMethodSignature> fixupProgramClass(
- DexProgramClass clazz, BiMap<DexMethodSignature, DexMethodSignature> remappedVirtualMethods) {
+ private DexMethodSignatureBiMap<DexMethodSignature> fixupProgramClass(
+ DexProgramClass clazz, DexMethodSignatureBiMap<DexMethodSignature> remappedVirtualMethods) {
assert !clazz.isInterface();
- // TODO(b/169395592): ensure merged classes have been removed using:
- // assert !mergedClasses.hasBeenMergedIntoDifferentType(clazz.type);
-
- BiMap<DexMethodSignature, DexMethodSignature> remappedClassVirtualMethods =
- HashBiMap.create(remappedVirtualMethods);
-
- Set<DexMethodSignature> newMethodReferences = Sets.newHashSet();
+ MutableBidirectionalOneToOneMap<DexEncodedMethod, DexMethodSignature> newMethodSignatures =
+ createLocallyReservedMethodSignatures(clazz, remappedVirtualMethods);
+ DexMethodSignatureBiMap<DexMethodSignature> remappedClassVirtualMethods =
+ new DexMethodSignatureBiMap<>(remappedVirtualMethods);
clazz
.getMethodCollection()
.replaceAllVirtualMethods(
- method -> fixupVirtualMethod(remappedClassVirtualMethods, newMethodReferences, method));
+ method ->
+ fixupVirtualMethod(
+ clazz, method, remappedClassVirtualMethods, newMethodSignatures));
clazz
.getMethodCollection()
- .replaceAllDirectMethods(method -> fixupDirectMethod(newMethodReferences, clazz, method));
+ .replaceAllDirectMethods(
+ method ->
+ fixupDirectMethod(clazz, method, remappedClassVirtualMethods, newMethodSignatures));
Set<DexField> newFieldReferences = Sets.newIdentityHashSet();
DexEncodedField[] instanceFields = clazz.clearInstanceFields();
@@ -185,10 +194,15 @@
}
private void fixupInterfaceClass(DexProgramClass iface) {
- Set<DexMethodSignature> newDirectMethods = new LinkedHashSet<>();
+ DexMethodSignatureBiMap<DexMethodSignature> remappedVirtualMethods =
+ DexMethodSignatureBiMap.empty();
+ MutableBidirectionalOneToOneMap<DexEncodedMethod, DexMethodSignature> newMethodSignatures =
+ new BidirectionalOneToOneHashMap<>();
iface
.getMethodCollection()
- .replaceDirectMethods(method -> fixupDirectMethod(newDirectMethods, iface, method));
+ .replaceDirectMethods(
+ method ->
+ fixupDirectMethod(iface, method, remappedVirtualMethods, newMethodSignatures));
iface.getMethodCollection().replaceVirtualMethods(this::fixupVirtualInterfaceMethod);
assert !iface.hasInstanceFields();
@@ -211,7 +225,18 @@
}
private DexEncodedMethod fixupProgramMethod(
- DexMethod newMethodReference, DexEncodedMethod method) {
+ DexProgramClass clazz, DexEncodedMethod method, DexMethod newMethodReference) {
+ // Convert out of DefaultInstanceInitializerCode, since this piece of code will require lens
+ // code rewriting.
+ if (isRunningBeforePrimaryOptimizationPass()
+ && method.hasCode()
+ && method.getCode().isDefaultInstanceInitializerCode()
+ && mergedClasses.isMergeSourceOrTarget(clazz.getSuperType())) {
+ DexType originalSuperType = originalSuperTypes.getOrDefault(clazz, clazz.getSuperType());
+ DefaultInstanceInitializerCode.uncanonicalizeCode(
+ appView, method.asProgramMethod(clazz), originalSuperType);
+ }
+
DexMethod originalMethodReference = method.getReference();
if (newMethodReference.isIdenticalTo(originalMethodReference)) {
return method;
@@ -232,85 +257,109 @@
}
private DexEncodedMethod fixupDirectMethod(
- Set<DexMethodSignature> newMethods, DexProgramClass clazz, DexEncodedMethod method) {
+ DexProgramClass clazz,
+ DexEncodedMethod method,
+ DexMethodSignatureBiMap<DexMethodSignature> remappedVirtualMethods,
+ MutableBidirectionalOneToOneMap<DexEncodedMethod, DexMethodSignature> newMethodSignatures) {
DexMethod originalMethodReference = method.getReference();
// Fix all type references in the method prototype.
- DexMethod newMethodReference = fixupMethodReference(originalMethodReference);
+ DexMethodSignature reservedMethodSignature = newMethodSignatures.get(method);
+ DexMethod newMethodReference;
+ if (reservedMethodSignature != null) {
+ newMethodReference = reservedMethodSignature.withHolder(clazz, dexItemFactory);
+ } else {
+ newMethodReference = fixupMethodReference(originalMethodReference);
+ if (newMethodSignatures.containsValue(newMethodReference.getSignature())) {
+ // If the method collides with a direct method on the same class then rename it to a
+ // globally
+ // fresh name and record the signature.
+ if (method.isInstanceInitializer()) {
+ // If the method is an instance initializer, then add extra nulls.
+ Box<Set<DexType>> usedSyntheticArgumentClasses = new Box<>();
+ newMethodReference =
+ dexItemFactory.createInstanceInitializerWithFreshProto(
+ newMethodReference,
+ syntheticArgumentClass.getArgumentClasses(),
+ tryMethod -> !newMethodSignatures.containsValue(tryMethod.getSignature()),
+ usedSyntheticArgumentClasses::set);
+ lensBuilder.addExtraParameters(
+ originalMethodReference,
+ newMethodReference,
+ ExtraUnusedNullParameter.computeExtraUnusedNullParameters(
+ originalMethodReference, newMethodReference));
- if (newMethods.contains(newMethodReference.getSignature())) {
- // If the method collides with a direct method on the same class then rename it to a globally
- // fresh name and record the signature.
-
- if (method.isInstanceInitializer()) {
- // If the method is an instance initializer, then add extra nulls.
- Box<Set<DexType>> usedSyntheticArgumentClasses = new Box<>();
- newMethodReference =
- dexItemFactory.createInstanceInitializerWithFreshProto(
- newMethodReference,
- syntheticArgumentClass.getArgumentClasses(),
- tryMethod -> !newMethods.contains(tryMethod.getSignature()),
- usedSyntheticArgumentClasses::set);
- lensBuilder.addExtraParameters(
- originalMethodReference,
- ExtraUnusedNullParameter.computeExtraUnusedNullParameters(
- originalMethodReference, newMethodReference));
-
- // Amend the art profile collection.
- if (usedSyntheticArgumentClasses.isSet()) {
- Set<DexMethod> previousMethodReferences =
- lensBuilder.getOriginalMethodReferences(originalMethodReference);
- if (previousMethodReferences.isEmpty()) {
- profileCollectionAdditions.applyIfContextIsInProfile(
- originalMethodReference,
- additionsBuilder ->
- usedSyntheticArgumentClasses.get().forEach(additionsBuilder::addRule));
- } else {
- for (DexMethod previousMethodReference : previousMethodReferences) {
+ // Amend the art profile collection.
+ if (usedSyntheticArgumentClasses.isSet()) {
+ Set<DexMethod> previousMethodReferences =
+ lensBuilder.getOriginalMethodReferences(originalMethodReference);
+ if (previousMethodReferences.isEmpty()) {
profileCollectionAdditions.applyIfContextIsInProfile(
- previousMethodReference,
+ originalMethodReference,
additionsBuilder ->
usedSyntheticArgumentClasses.get().forEach(additionsBuilder::addRule));
+ } else {
+ for (DexMethod previousMethodReference : previousMethodReferences) {
+ profileCollectionAdditions.applyIfContextIsInProfile(
+ previousMethodReference,
+ additionsBuilder ->
+ usedSyntheticArgumentClasses.get().forEach(additionsBuilder::addRule));
+ }
}
}
+ } else {
+ newMethodReference =
+ dexItemFactory.createFreshMethodNameWithoutHolder(
+ newMethodReference.getName().toSourceString(),
+ newMethodReference.getProto(),
+ newMethodReference.getHolderType(),
+ tryMethod ->
+ !reservedInterfaceSignatures.containsValue(tryMethod.getSignature())
+ && !remappedVirtualMethods.containsValue(tryMethod.getSignature())
+ && !newMethodSignatures.containsValue(tryMethod.getSignature()));
}
- } else {
- newMethodReference =
- dexItemFactory.createFreshMethodNameWithoutHolder(
- newMethodReference.getName().toSourceString(),
- newMethodReference.proto,
- newMethodReference.holder,
- tryMethod ->
- !reservedInterfaceSignatures.containsValue(tryMethod.getSignature())
- && !newMethods.contains(tryMethod.getSignature()));
+ }
+
+ assert !newMethodSignatures.containsValue(newMethodReference.getSignature());
+ newMethodSignatures.put(method, newMethodReference.getSignature());
+ }
+
+ return fixupProgramMethod(clazz, method, newMethodReference);
+ }
+
+ private MutableBidirectionalOneToOneMap<DexEncodedMethod, DexMethodSignature>
+ createLocallyReservedMethodSignatures(
+ DexProgramClass clazz,
+ DexMethodSignatureBiMap<DexMethodSignature> remappedVirtualMethods) {
+ MutableBidirectionalOneToOneMap<DexEncodedMethod, DexMethodSignature> newMethodSignatures =
+ new BidirectionalOneToOneHashMap<>();
+ for (DexEncodedMethod method : clazz.methods()) {
+ if (method.belongsToVirtualPool()) {
+ DexMethodSignature reservedMethodSignature =
+ lookupReservedVirtualName(method, remappedVirtualMethods);
+ if (reservedMethodSignature != null) {
+ newMethodSignatures.put(method, reservedMethodSignature);
+ continue;
+ }
+ }
+ // Reserve the method signature if it is unchanged and not globally reserved.
+ DexMethodSignature newMethodSignature = fixupMethodSignature(method);
+ if (newMethodSignature.equals(method.getName(), method.getProto())
+ && !reservedInterfaceSignatures.containsValue(newMethodSignature)
+ && !remappedVirtualMethods.containsValue(newMethodSignature)) {
+ newMethodSignatures.put(method, newMethodSignature);
}
}
-
- boolean changed = newMethods.add(newMethodReference.getSignature());
- assert changed;
-
- // Convert out of DefaultInstanceInitializerCode, since this piece of code will require lens
- // code rewriting.
- if (isRunningBeforePrimaryOptimizationPass()
- && method.hasCode()
- && method.getCode().isDefaultInstanceInitializerCode()
- && mergedClasses.isMergeSourceOrTarget(clazz.getSuperType())) {
- DexType originalSuperType = originalSuperTypes.getOrDefault(clazz, clazz.getSuperType());
- DefaultInstanceInitializerCode.uncanonicalizeCode(
- appView, method.asProgramMethod(clazz), originalSuperType);
- }
-
- return fixupProgramMethod(newMethodReference, method);
+ return newMethodSignatures;
}
private DexMethodSignature lookupReservedVirtualName(
- DexMethod originalMethodReference,
- BiMap<DexMethodSignature, DexMethodSignature> renamedClassVirtualMethods) {
- DexMethodSignature originalSignature = originalMethodReference.getSignature();
+ DexEncodedMethod method,
+ DexMethodSignatureBiMap<DexMethodSignature> renamedClassVirtualMethods) {
+ DexMethodSignature originalSignature = method.getSignature();
// Determine if the original method has been rewritten by a parent class
DexMethodSignature renamedVirtualName = renamedClassVirtualMethods.get(originalSignature);
-
if (renamedVirtualName == null) {
// Determine if there is a signature mapping.
DexMethodSignature mappedInterfaceSignature =
@@ -321,63 +370,40 @@
} else {
assert !reservedInterfaceSignatures.containsKey(originalSignature);
}
-
return renamedVirtualName;
}
private DexEncodedMethod fixupVirtualMethod(
- BiMap<DexMethodSignature, DexMethodSignature> renamedClassVirtualMethods,
- Set<DexMethodSignature> newMethods,
- DexEncodedMethod method) {
- DexMethod originalMethodReference = method.getReference();
- DexMethodSignature originalSignature = originalMethodReference.getSignature();
-
- DexMethodSignature renamedVirtualName =
- lookupReservedVirtualName(originalMethodReference, renamedClassVirtualMethods);
-
- // Fix all type references in the method prototype.
- DexMethodSignature newSignature = fixupMethodSignature(method);
-
- if (renamedVirtualName != null) {
- // If the method was renamed in a parent, rename it in the child.
- newSignature = renamedVirtualName;
-
- assert !newMethods.contains(newSignature);
- } else if (reservedInterfaceSignatures.containsValue(newSignature)
- || newMethods.contains(newSignature)
- || renamedClassVirtualMethods.containsValue(newSignature)) {
- // If the method potentially collides with an interface method or with another virtual method
- // rename it to a globally fresh name and record the name.
-
+ DexProgramClass clazz,
+ DexEncodedMethod method,
+ DexMethodSignatureBiMap<DexMethodSignature> renamedClassVirtualMethods,
+ MutableBidirectionalOneToOneMap<DexEncodedMethod, DexMethodSignature> newMethodSignatures) {
+ DexMethodSignature newSignature = newMethodSignatures.get(method);
+ if (newSignature == null) {
+ // Fix all type references in the method prototype.
newSignature =
dexItemFactory.createFreshMethodSignatureName(
- originalMethodReference.getName().toSourceString(),
+ method.getName().toSourceString(),
null,
- newSignature.getProto(),
+ fixupProto(method.getProto()),
trySignature ->
!reservedInterfaceSignatures.containsValue(trySignature)
- && !newMethods.contains(trySignature)
+ && !newMethodSignatures.containsValue(trySignature)
&& !renamedClassVirtualMethods.containsValue(trySignature));
-
- // Record signature renaming so that subclasses perform the identical rename.
- renamedClassVirtualMethods.put(originalSignature, newSignature);
- } else {
- // There was no reserved name and the new signature is available.
-
- if (Iterables.any(
- newSignature.getProto().getParameterBaseTypes(dexItemFactory),
- mergedClasses::isMergeTarget)) {
- // If any of the parameter types have been merged, record the signature mapping.
- renamedClassVirtualMethods.put(originalSignature, newSignature);
- }
+ newMethodSignatures.put(method, newSignature);
}
- boolean changed = newMethods.add(newSignature);
- assert changed;
+ // If any of the parameter types have been merged, record the signature mapping so that
+ // subclasses perform the identical rename.
+ if (!reservedInterfaceSignatures.containsKey(method)
+ && Iterables.any(
+ newSignature.getProto().getParameterBaseTypes(dexItemFactory),
+ mergedClasses::isMergeTarget)) {
+ renamedClassVirtualMethods.put(method.getSignature(), newSignature);
+ }
- DexMethod newMethodReference =
- newSignature.withHolder(fixupType(originalMethodReference.holder), dexItemFactory);
- return fixupProgramMethod(newMethodReference, method);
+ DexMethod newMethodReference = newSignature.withHolder(clazz, dexItemFactory);
+ return fixupProgramMethod(clazz, method, newMethodReference);
}
@SuppressWarnings("ReferenceEquality")
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java b/src/main/java/com/android/tools/r8/classmerging/SyntheticArgumentClass.java
similarity index 78%
rename from src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
rename to src/main/java/com/android/tools/r8/classmerging/SyntheticArgumentClass.java
index 1074a5c..a8dcd49 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
+++ b/src/main/java/com/android/tools/r8/classmerging/SyntheticArgumentClass.java
@@ -1,12 +1,13 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2023, 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.horizontalclassmerging;
+package com.android.tools.r8.classmerging;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.horizontalclassmerging.MergeGroup;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.synthesis.SyntheticItems.SyntheticKindSelector;
import com.google.common.base.Suppliers;
@@ -48,7 +49,7 @@
private final AppView<AppInfoWithLiveness> appView;
- Builder(AppView<AppInfoWithLiveness> appView) {
+ public Builder(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
}
@@ -60,22 +61,28 @@
}
public SyntheticArgumentClass build(Collection<MergeGroup> mergeGroups) {
- DexProgramClass context = getDeterministicContext(mergeGroups);
+ return build(getDeterministicContext(mergeGroups));
+ }
+
+ public SyntheticArgumentClass build(DexProgramClass deterministicContext) {
List<Supplier<DexType>> syntheticArgumentTypes = new ArrayList<>();
syntheticArgumentTypes.add(
Suppliers.memoize(
() ->
- synthesizeClass(context, kinds -> kinds.HORIZONTAL_INIT_TYPE_ARGUMENT_1)
+ synthesizeClass(
+ deterministicContext, kinds -> kinds.HORIZONTAL_INIT_TYPE_ARGUMENT_1)
.getType()));
syntheticArgumentTypes.add(
Suppliers.memoize(
() ->
- synthesizeClass(context, kinds -> kinds.HORIZONTAL_INIT_TYPE_ARGUMENT_2)
+ synthesizeClass(
+ deterministicContext, kinds -> kinds.HORIZONTAL_INIT_TYPE_ARGUMENT_2)
.getType()));
syntheticArgumentTypes.add(
Suppliers.memoize(
() ->
- synthesizeClass(context, kinds -> kinds.HORIZONTAL_INIT_TYPE_ARGUMENT_3)
+ synthesizeClass(
+ deterministicContext, kinds -> kinds.HORIZONTAL_INIT_TYPE_ARGUMENT_3)
.getType()));
return new SyntheticArgumentClass(syntheticArgumentTypes);
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 693d2ac..3e0bc95 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.analysis.InitializedClassesInInstanceMethodsAnalysis.InitializedClassesInInstanceMethods;
import com.android.tools.r8.graph.analysis.ResourceAccessAnalysis.ResourceAnalysisResult;
import com.android.tools.r8.graph.classmerging.MergedClassesCollection;
+import com.android.tools.r8.graph.lens.AppliedGraphLens;
import com.android.tools.r8.graph.lens.ClearCodeRewritingGraphLens;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.graph.lens.InitClassLens;
@@ -38,6 +39,8 @@
import com.android.tools.r8.ir.optimize.library.LibraryMethodSideEffectModelCollection;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.naming.SeedMapper;
+import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
+import com.android.tools.r8.optimize.MemberRebindingIdentityLensFactory;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
import com.android.tools.r8.optimize.compose.ComposeReferences;
import com.android.tools.r8.optimize.interfaces.collection.OpenClosedInterfacesCollection;
@@ -434,8 +437,18 @@
allCodeProcessed = true;
}
- public void clearCodeRewritings() {
+ public void clearCodeRewritings(ExecutorService executorService) throws ExecutionException {
setGraphLens(new ClearCodeRewritingGraphLens(withClassHierarchy()));
+
+ MemberRebindingIdentityLens memberRebindingIdentityLens =
+ MemberRebindingIdentityLensFactory.rebuild(withClassHierarchy(), executorService);
+ setGraphLens(memberRebindingIdentityLens);
+ }
+
+ public void flattenGraphLenses() {
+ GraphLens graphLens = graphLens();
+ setGraphLens(GraphLens.getIdentityLens());
+ setGraphLens(new AppliedGraphLens(withClassHierarchy(), graphLens));
}
public AppServices appServices() {
@@ -1246,19 +1259,13 @@
if (!firstUnappliedLens.isMemberRebindingLens()
&& !firstUnappliedLens.isMemberRebindingIdentityLens()) {
NonIdentityGraphLens appliedMemberRebindingLens =
- firstUnappliedLens.findPrevious(
- previous ->
- previous.isMemberRebindingLens() || previous.isMemberRebindingIdentityLens());
+ firstUnappliedLens.findPrevious(GraphLens::isMemberRebindingIdentityLens);
if (appliedMemberRebindingLens != null) {
newMemberRebindingLens =
- appliedMemberRebindingLens.isMemberRebindingLens()
- ? appliedMemberRebindingLens
- .asMemberRebindingLens()
- .toRewrittenFieldRebindingLens(appView, appliedLens, appliedMemberRebindingLens)
- : appliedMemberRebindingLens
- .asMemberRebindingIdentityLens()
- .toRewrittenMemberRebindingIdentityLens(
- appView, appliedLens, appliedMemberRebindingLens);
+ appliedMemberRebindingLens
+ .asMemberRebindingIdentityLens()
+ .toRewrittenMemberRebindingIdentityLens(
+ appView, appliedLens, appliedMemberRebindingLens);
}
}
timing.end();
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 77398b6..3b8ba07 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -854,13 +854,6 @@
GraphLens graphLens,
ProgramMethod context) {
InliningConstraints inliningConstraints = new InliningConstraints(appView, graphLens);
- if (appView.options().isInterfaceMethodDesugaringEnabled()) {
- // TODO(b/120130831): Conservatively need to say "no" at this point if there are invocations
- // to static interface methods. This should be fixed by making sure that the desugared
- // versions of default and static interface methods are present in the application during
- // IR processing.
- inliningConstraints.disallowStaticInterfaceMethodCalls();
- }
ConstraintWithTarget constraint = ConstraintWithTarget.ALWAYS;
assert inliningConstraints.forMonitor().isAlways();
for (CfInstruction insn : instructions) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexMember.java b/src/main/java/com/android/tools/r8/graph/DexMember.java
index a484fdd..88e91d8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMember.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.graph;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Streams;
import java.util.function.Function;
+import java.util.function.Predicate;
public abstract class DexMember<D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
extends DexReference implements NamingLensComparable<R> {
@@ -70,5 +72,11 @@
return Iterables.transform(getReferencedTypes(), type -> type.toBaseType(dexItemFactory));
}
+ public boolean verifyReferencedBaseTypesMatches(
+ Predicate<DexType> predicate, DexItemFactory dexItemFactory) {
+ assert Streams.stream(getReferencedBaseTypes(dexItemFactory)).allMatch(predicate);
+ return true;
+ }
+
public abstract DexMember<D, R> withHolder(DexReference holder, DexItemFactory dexItemFactory);
}
diff --git a/src/main/java/com/android/tools/r8/graph/lens/AppliedGraphLens.java b/src/main/java/com/android/tools/r8/graph/lens/AppliedGraphLens.java
index 866cc8a..2c0c5bc 100644
--- a/src/main/java/com/android/tools/r8/graph/lens/AppliedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/lens/AppliedGraphLens.java
@@ -45,22 +45,23 @@
private final Map<DexMethod, DexMethod> extraOriginalMethodSignatures = new IdentityHashMap<>();
@SuppressWarnings("ReferenceEquality")
- public AppliedGraphLens(AppView<? extends AppInfoWithClassHierarchy> appView) {
- super(appView, GraphLens.getIdentityLens());
+ public AppliedGraphLens(
+ AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens graphLens) {
+ super(appView);
for (DexProgramClass clazz : appView.appInfo().classes()) {
// TODO(b/169395592): If merged classes were removed from the application this would not be
// necessary.
- if (appView.graphLens().lookupType(clazz.getType()) != clazz.getType()) {
+ if (graphLens.lookupType(clazz.getType()) != clazz.getType()) {
continue;
}
// Record original type names.
- recordOriginalTypeNames(clazz, appView);
+ recordOriginalTypeNames(clazz, graphLens);
// Record original field signatures.
for (DexEncodedField encodedField : clazz.fields()) {
DexField field = encodedField.getReference();
- DexField original = appView.graphLens().getOriginalFieldSignature(field);
+ DexField original = graphLens.getOriginalFieldSignature(field);
if (original != field) {
DexField existing = originalFieldSignatures.forcePut(field, original);
assert existing == null;
@@ -70,12 +71,12 @@
// Record original method signatures.
for (DexEncodedMethod encodedMethod : clazz.methods()) {
DexMethod method = encodedMethod.getReference();
- DexMethod original = appView.graphLens().getOriginalMethodSignatureForMapping(method);
+ DexMethod original = graphLens.getOriginalMethodSignatureForMapping(method);
DexMethod existing = originalMethodSignatures.inverse().get(original);
if (existing == null) {
originalMethodSignatures.put(method, original);
} else {
- DexMethod renamed = appView.graphLens().getRenamedMethodSignature(original);
+ DexMethod renamed = graphLens.getRenamedMethodSignature(original);
if (renamed == existing) {
extraOriginalMethodSignatures.put(method, original);
} else {
@@ -92,11 +93,10 @@
}
@SuppressWarnings("ReferenceEquality")
- private void recordOriginalTypeNames(
- DexProgramClass clazz, AppView<? extends AppInfoWithClassHierarchy> appView) {
+ private void recordOriginalTypeNames(DexProgramClass clazz, GraphLens graphLens) {
DexType type = clazz.getType();
- List<DexType> originalTypes = Lists.newArrayList(appView.graphLens().getOriginalTypes(type));
+ List<DexType> originalTypes = Lists.newArrayList(graphLens.getOriginalTypes(type));
boolean isIdentity = originalTypes.size() == 1 && originalTypes.get(0) == type;
if (!isIdentity) {
originalTypes.forEach(
@@ -104,7 +104,7 @@
assert !renamedTypeNames.containsKey(originalType);
renamedTypeNames.put(originalType, type);
});
- renamedTypeNames.setRepresentative(type, appView.graphLens().getOriginalType(type));
+ renamedTypeNames.setRepresentative(type, graphLens.getOriginalType(type));
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/lens/DefaultNonIdentityGraphLens.java b/src/main/java/com/android/tools/r8/graph/lens/DefaultNonIdentityGraphLens.java
index df70495..26376cd 100644
--- a/src/main/java/com/android/tools/r8/graph/lens/DefaultNonIdentityGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/lens/DefaultNonIdentityGraphLens.java
@@ -67,7 +67,7 @@
@Override
protected MethodLookupResult internalDescribeLookupMethod(
MethodLookupResult previous, DexMethod context, GraphLens codeLens) {
- return previous;
+ return previous.verify(this, codeLens);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java b/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java
index a7ef09b..b0292c6 100644
--- a/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java
@@ -225,12 +225,6 @@
public abstract String lookupPackageName(String pkg);
- @Deprecated
- public final DexType lookupClassType(DexType type) {
- GraphLens appliedLens = getIdentityLens();
- return lookupClassType(type, appliedLens);
- }
-
public final DexType lookupClassType(DexType type, GraphLens appliedLens) {
return getRenamedReference(
type, appliedLens, NonIdentityGraphLens::getNextClassType, DexType::isPrimitiveType);
@@ -322,7 +316,7 @@
GraphLens codeLens,
LookupMethodContinuation continuation);
- interface LookupMethodContinuation {
+ public interface LookupMethodContinuation {
MethodLookupResult lookupMethod(MethodLookupResult previous);
}
diff --git a/src/main/java/com/android/tools/r8/graph/lens/IdentityGraphLens.java b/src/main/java/com/android/tools/r8/graph/lens/IdentityGraphLens.java
index e72f128..1497895 100644
--- a/src/main/java/com/android/tools/r8/graph/lens/IdentityGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/lens/IdentityGraphLens.java
@@ -55,7 +55,7 @@
public MethodLookupResult lookupMethod(
DexMethod method, DexMethod context, InvokeType type, GraphLens codeLens) {
assert codeLens == null || codeLens.isIdentityLens();
- return MethodLookupResult.builder(this).setReference(method).setType(type).build();
+ return MethodLookupResult.builder(this, codeLens).setReference(method).setType(type).build();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/lens/MemberLookupResult.java b/src/main/java/com/android/tools/r8/graph/lens/MemberLookupResult.java
index 7ec7c85..9b89ff1 100644
--- a/src/main/java/com/android/tools/r8/graph/lens/MemberLookupResult.java
+++ b/src/main/java/com/android/tools/r8/graph/lens/MemberLookupResult.java
@@ -4,7 +4,11 @@
package com.android.tools.r8.graph.lens;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMember;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.ObjectUtils;
+import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
import java.util.Map;
import java.util.function.Function;
@@ -31,6 +35,20 @@
return rewritings.getOrDefault(reference, reference);
}
+ @SuppressWarnings("unchecked")
+ public R getRewrittenReferenceFromRewrittenReboundReference(
+ R rewrittenReboundReference,
+ Function<DexType, DexType> typeRewriter,
+ DexItemFactory dexItemFactory) {
+ R rewrittenReference =
+ ObjectUtils.identical(reference, reboundReference)
+ ? rewrittenReboundReference
+ : (R)
+ rewrittenReboundReference.withHolder(
+ typeRewriter.apply(reference.getHolderType()), dexItemFactory);
+ return rewrittenReference;
+ }
+
public boolean hasReboundReference() {
return reboundReference != null;
}
@@ -39,7 +57,11 @@
return reboundReference;
}
- public R getRewrittenReboundReference(BidirectionalManyToOneRepresentativeMap<R, R> rewritings) {
+ public R getRewrittenReboundReference(BidirectionalManyToManyRepresentativeMap<R, R> rewritings) {
+ return rewritings.getRepresentativeValueOrDefault(reboundReference, reboundReference);
+ }
+
+ public R getRewrittenReboundReference(Map<R, R> rewritings) {
return rewritings.getOrDefault(reboundReference, reboundReference);
}
@@ -53,11 +75,19 @@
R reference;
R reboundReference;
+ public R getReference() {
+ return reference;
+ }
+
public Self setReference(R reference) {
this.reference = reference;
return self();
}
+ public R getReboundReference() {
+ return reboundReference;
+ }
+
public Self setReboundReference(R reboundReference) {
this.reboundReference = reboundReference;
return self();
diff --git a/src/main/java/com/android/tools/r8/graph/lens/MethodLookupResult.java b/src/main/java/com/android/tools/r8/graph/lens/MethodLookupResult.java
index d0c3ded..450585d 100644
--- a/src/main/java/com/android/tools/r8/graph/lens/MethodLookupResult.java
+++ b/src/main/java/com/android/tools/r8/graph/lens/MethodLookupResult.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.code.InvokeType;
+import com.android.tools.r8.optimize.bridgehoisting.BridgeHoistingLens;
/**
* Result of a method lookup in a GraphLens.
@@ -30,29 +31,44 @@
this.prototypeChanges = prototypeChanges;
}
- public static Builder builder(GraphLens lens) {
- return new Builder(lens);
+ public static Builder builder(GraphLens lens, GraphLens codeLens) {
+ return new Builder(lens, codeLens);
}
public InvokeType getType() {
return type;
}
- @SuppressWarnings("UnusedVariable")
public RewrittenPrototypeDescription getPrototypeChanges() {
return prototypeChanges;
}
+ public MethodLookupResult verify(GraphLens lens, GraphLens codeLens) {
+ assert getReference() != null;
+ assert lens.isIdentityLens()
+ || lens.isAppliedLens()
+ || lens.asNonIdentityLens().isD8Lens()
+ || getReference().getHolderType().isArrayType()
+ || hasReboundReference()
+ // TODO: Disallow the following.
+ || lens.isEnumUnboxerLens()
+ || lens.isNumberUnboxerLens()
+ || lens instanceof BridgeHoistingLens
+ : lens;
+ return this;
+ }
+
public static class Builder extends MemberLookupResult.Builder<DexMethod, Builder> {
- @SuppressWarnings("UnusedVariable")
private final GraphLens lens;
+ private final GraphLens codeLens;
private RewrittenPrototypeDescription prototypeChanges = RewrittenPrototypeDescription.none();
private InvokeType type;
- private Builder(GraphLens lens) {
+ private Builder(GraphLens lens, GraphLens codeLens) {
this.lens = lens;
+ this.codeLens = codeLens;
}
public Builder setPrototypeChanges(RewrittenPrototypeDescription prototypeChanges) {
@@ -66,9 +82,8 @@
}
public MethodLookupResult build() {
- assert reference != null;
- // TODO(b/168282032): All non-identity graph lenses should set the rebound reference.
- return new MethodLookupResult(reference, reboundReference, type, prototypeChanges);
+ return new MethodLookupResult(reference, reboundReference, type, prototypeChanges)
+ .verify(lens, codeLens);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/lens/NestedGraphLens.java b/src/main/java/com/android/tools/r8/graph/lens/NestedGraphLens.java
index 63aa73a..1186ce2 100644
--- a/src/main/java/com/android/tools/r8/graph/lens/NestedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/lens/NestedGraphLens.java
@@ -172,12 +172,14 @@
: // This assumes that the holder will always be moved in lock-step with the method!
rewrittenReboundReference.withHolder(
getNextClassType(previous.getReference().getHolderType()), dexItemFactory());
- return MethodLookupResult.builder(this)
+ return MethodLookupResult.builder(this, codeLens)
.setReference(rewrittenReference)
.setReboundReference(rewrittenReboundReference)
.setPrototypeChanges(
internalDescribePrototypeChanges(
- previous.getPrototypeChanges(), rewrittenReboundReference))
+ previous.getPrototypeChanges(),
+ previous.getReboundReference(),
+ rewrittenReboundReference))
.setType(
mapInvocationType(
rewrittenReboundReference, previous.getReference(), previous.getType()))
@@ -190,14 +192,15 @@
newMethod = previous.getReference();
}
RewrittenPrototypeDescription newPrototypeChanges =
- internalDescribePrototypeChanges(previous.getPrototypeChanges(), newMethod);
+ internalDescribePrototypeChanges(
+ previous.getPrototypeChanges(), previous.getReference(), newMethod);
if (newMethod == previous.getReference()
&& newPrototypeChanges == previous.getPrototypeChanges()) {
- return previous;
+ return previous.verify(this, codeLens);
}
// TODO(sgjesse): Should we always do interface to virtual mapping? Is it a performance win
// that only subclasses which are known to need it actually do it?
- return MethodLookupResult.builder(this)
+ return MethodLookupResult.builder(this, codeLens)
.setReference(newMethod)
.setPrototypeChanges(newPrototypeChanges)
.setType(mapInvocationType(newMethod, previous.getReference(), previous.getType()))
@@ -214,11 +217,13 @@
DexMethod previous = getPreviousMethodSignature(method);
RewrittenPrototypeDescription lookup =
getPrevious().lookupPrototypeChangesForMethodDefinition(previous, codeLens);
- return internalDescribePrototypeChanges(lookup, method);
+ return internalDescribePrototypeChanges(lookup, previous, method);
}
protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
- RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
+ RewrittenPrototypeDescription prototypeChanges,
+ DexMethod previousMethod,
+ DexMethod newMethod) {
return prototypeChanges;
}
diff --git a/src/main/java/com/android/tools/r8/graph/lens/NonIdentityGraphLens.java b/src/main/java/com/android/tools/r8/graph/lens/NonIdentityGraphLens.java
index 07fac2c..987bfe3 100644
--- a/src/main/java/com/android/tools/r8/graph/lens/NonIdentityGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/lens/NonIdentityGraphLens.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.InvokeType;
import com.android.tools.r8.utils.ThrowingAction;
+import com.google.common.collect.Streams;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
@@ -82,20 +83,19 @@
}
@Override
- @SuppressWarnings("ReferenceEquality")
public MethodLookupResult lookupMethod(
- DexMethod method, DexMethod context, InvokeType type, GraphLens codeLens) {
+ DexMethod method, DexMethod context, InvokeType invokeType, GraphLens codeLens) {
if (method.getHolderType().isArrayType()) {
- assert lookupType(method.getReturnType()) == method.getReturnType();
- assert method.getParameters().stream()
- .allMatch(parameterType -> lookupType(parameterType) == parameterType);
- return MethodLookupResult.builder(this)
- .setReference(method.withHolder(lookupType(method.getHolderType()), dexItemFactory))
- .setType(type)
+ assert Streams.stream(method.getReferencedBaseTypes(dexItemFactory))
+ .allMatch(type -> type.isIdenticalTo(lookupClassType(type, codeLens)));
+ return MethodLookupResult.builder(this, codeLens)
+ .setReference(
+ method.withHolder(lookupType(method.getHolderType(), codeLens), dexItemFactory))
+ .setType(invokeType)
.build();
}
assert method.getHolderType().isClassType();
- return internalLookupMethod(method, context, type, codeLens, result -> result);
+ return internalLookupMethod(method, context, invokeType, codeLens, result -> result);
}
@Override
@@ -186,6 +186,10 @@
public abstract DexMethod getNextMethodSignature(DexMethod method);
+ public final boolean isD8Lens() {
+ return !appView.enableWholeProgramOptimizations();
+ }
+
@Override
public final boolean isIdentityLens() {
return false;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index d6b92e0..ecbf167 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -7,6 +7,7 @@
import static com.google.common.base.Predicates.not;
import com.android.tools.r8.androidapi.ComputedApiLevel;
+import com.android.tools.r8.classmerging.SyntheticArgumentClass;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexDefinition;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 141e7f6..57ff66a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.graph.DexClassAndMethod.asProgramMethodOrNull;
+import com.android.tools.r8.classmerging.SyntheticArgumentClass;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
@@ -14,9 +15,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.lens.MethodLookupResult;
import com.android.tools.r8.horizontalclassmerging.code.SyntheticInitializerConverter;
-import com.android.tools.r8.ir.code.InvokeType;
import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
import com.android.tools.r8.profile.art.ArtProfileCompletenessChecker;
@@ -175,7 +174,8 @@
mode,
profileCollectionAdditions,
syntheticArgumentClass,
- executorService);
+ executorService,
+ timing);
profileCollectionAdditions =
profileCollectionAdditions.rewriteMethodReferences(
horizontalClassMergerGraphLens::getNextMethodToInvoke);
@@ -255,14 +255,10 @@
for (VirtuallyMergedMethodsKeepInfo virtuallyMergedMethodsKeepInfo :
virtuallyMergedMethodsKeepInfos) {
DexMethod representative = virtuallyMergedMethodsKeepInfo.getRepresentative();
- MethodLookupResult lookupResult =
- horizontalClassMergerGraphLens.lookupMethod(
- representative,
- null,
- InvokeType.VIRTUAL,
- horizontalClassMergerGraphLens.getPrevious());
+ DexMethod mergedMethodReference =
+ horizontalClassMergerGraphLens.getNextMethodToInvoke(representative);
ProgramMethod mergedMethod =
- asProgramMethodOrNull(appView.definitionFor(lookupResult.getReference()));
+ asProgramMethodOrNull(appView.definitionFor(mergedMethodReference));
if (mergedMethod != null) {
keepInfo.joinMethod(
mergedMethod,
@@ -427,7 +423,8 @@
Mode mode,
ProfileCollectionAdditions profileCollectionAdditions,
SyntheticArgumentClass syntheticArgumentClass,
- ExecutorService executorService)
+ ExecutorService executorService,
+ Timing timing)
throws ExecutionException {
return new HorizontalClassMergerTreeFixer(
appView,
@@ -436,7 +433,7 @@
mode,
profileCollectionAdditions,
syntheticArgumentClass)
- .run(executorService);
+ .run(executorService, timing);
}
@SuppressWarnings("ReferenceEquality")
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
index 200a943..24e3f18 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -15,9 +15,11 @@
import com.android.tools.r8.graph.lens.FieldLookupResult;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.graph.lens.MethodLookupResult;
+import com.android.tools.r8.ir.code.InvokeType;
import com.android.tools.r8.ir.conversion.ExtraParameter;
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeHashMap;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap;
@@ -40,9 +42,14 @@
HorizontallyMergedClasses mergedClasses,
Map<DexMethod, List<ExtraParameter>> methodExtraParameters,
BidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap,
- Map<DexMethod, DexMethod> methodMap,
+ BidirectionalManyToOneMap<DexMethod, DexMethod> methodMap,
BidirectionalManyToOneRepresentativeMap<DexMethod, DexMethod> newMethodSignatures) {
- super(appView, fieldMap, methodMap, mergedClasses.getBidirectionalMap(), newMethodSignatures);
+ super(
+ appView,
+ fieldMap,
+ methodMap.getForwardMap(),
+ mergedClasses.getBidirectionalMap(),
+ newMethodSignatures);
this.methodExtraParameters = methodExtraParameters;
this.mergedClasses = mergedClasses;
}
@@ -67,6 +74,32 @@
return IterableUtils.prependSingleton(previous, mergedClasses.getSourcesFor(previous));
}
+ @Override
+ protected MethodLookupResult internalLookupMethod(
+ DexMethod reference,
+ DexMethod context,
+ InvokeType type,
+ GraphLens codeLens,
+ LookupMethodContinuation continuation) {
+ if (this == codeLens) {
+ // We sometimes create code objects that have the HorizontalClassMergerGraphLens as code lens.
+ // When using this lens as a code lens there is no lens that will insert the rebound reference
+ // since the MemberRebindingIdentityLens is an ancestor of the HorizontalClassMergerGraphLens.
+ // We therefore use the reference itself as the rebound reference here, which is safe since
+ // the code objects created during horizontal class merging are guaranteed not to contain
+ // any non-rebound method references.
+ // TODO(b/315284255): Actually guarantee the above!
+ MethodLookupResult lookupResult =
+ MethodLookupResult.builder(this, codeLens)
+ .setReboundReference(reference)
+ .setReference(reference)
+ .setType(type)
+ .build();
+ return continuation.lookupMethod(lookupResult);
+ }
+ return super.internalLookupMethod(reference, context, type, codeLens, continuation);
+ }
+
/**
* If an overloaded constructor is requested, add the constructor id as a parameter to the
* constructor. Otherwise return the lookup on the underlying graph lens.
@@ -74,12 +107,18 @@
@Override
public MethodLookupResult internalDescribeLookupMethod(
MethodLookupResult previous, DexMethod context, GraphLens codeLens) {
- List<ExtraParameter> extraParameters = methodExtraParameters.get(previous.getReference());
+ if (!previous.hasReboundReference()) {
+ return super.internalDescribeLookupMethod(previous, context, codeLens);
+ }
+ assert previous.hasReboundReference();
+ List<ExtraParameter> extraParameters =
+ methodExtraParameters.get(previous.getReboundReference());
MethodLookupResult lookup = super.internalDescribeLookupMethod(previous, context, codeLens);
if (extraParameters == null) {
return lookup;
}
- return MethodLookupResult.builder(this)
+ return MethodLookupResult.builder(this, codeLens)
+ .setReboundReference(lookup.getReboundReference())
.setReference(lookup.getReference())
.setPrototypeChanges(lookup.getPrototypeChanges().withExtraParameters(extraParameters))
.setType(lookup.getType())
@@ -144,7 +183,7 @@
mergedClasses,
methodExtraParameters,
newFieldSignatures,
- methodMap.getForwardMap(),
+ methodMap,
newMethodSignatures);
}
@@ -237,9 +276,7 @@
if (originalMethodSignatures.isEmpty()) {
pendingMethodMapUpdates.put(oldMethodSignature, newMethodSignature);
} else {
- for (DexMethod originalMethodSignature : originalMethodSignatures) {
- pendingMethodMapUpdates.put(originalMethodSignature, newMethodSignature);
- }
+ pendingMethodMapUpdates.put(originalMethodSignatures, newMethodSignature);
}
}
@@ -253,9 +290,7 @@
if (oldMemberSignatures.isEmpty()) {
pendingNewMemberSignatureUpdates.put(oldMemberSignature, newMemberSignature);
} else {
- for (R originalMethodSignature : oldMemberSignatures) {
- pendingNewMemberSignatureUpdates.put(originalMethodSignature, newMemberSignature);
- }
+ pendingNewMemberSignatureUpdates.put(oldMemberSignatures, newMemberSignature);
R representative = newMemberSignatures.getRepresentativeKey(oldMemberSignature);
if (representative != null) {
pendingNewMemberSignatureUpdates.setRepresentative(newMemberSignature, representative);
@@ -298,11 +333,11 @@
@Override
public void addExtraParameters(
- DexMethod methodSignature, List<? extends ExtraParameter> extraParameters) {
- Set<DexMethod> originalMethodSignatures = methodMap.getKeys(methodSignature);
+ DexMethod from, DexMethod to, List<? extends ExtraParameter> extraParameters) {
+ Set<DexMethod> originalMethodSignatures = methodMap.getKeys(from);
if (originalMethodSignatures.isEmpty()) {
methodExtraParameters
- .computeIfAbsent(methodSignature, ignore -> new ArrayList<>(extraParameters.size()))
+ .computeIfAbsent(from, ignore -> new ArrayList<>(extraParameters.size()))
.addAll(extraParameters);
} else {
for (DexMethod originalMethodSignature : originalMethodSignatures) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerTreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerTreeFixer.java
index 260ec11..fb27321 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerTreeFixer.java
@@ -5,9 +5,11 @@
package com.android.tools.r8.horizontalclassmerging;
import com.android.tools.r8.classmerging.ClassMergerTreeFixer;
+import com.android.tools.r8.classmerging.SyntheticArgumentClass;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
+import com.android.tools.r8.utils.Timing;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -89,9 +91,9 @@
* </ul>
*/
@Override
- public HorizontalClassMergerGraphLens run(ExecutorService executorService)
+ public HorizontalClassMergerGraphLens run(ExecutorService executorService, Timing timing)
throws ExecutionException {
- return super.run(executorService);
+ return super.run(executorService, timing);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java
index 837157e..e09f988 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java
@@ -102,9 +102,7 @@
int classIdLocalIndex = maxLocals - 1 - extraNulls;
instructionBuilder.add(new CfLoad(ValueType.OBJECT, 0));
instructionBuilder.add(new CfLoad(ValueType.INT, classIdLocalIndex));
- instructionBuilder.add(
- new CfInstanceFieldWrite(
- lens.getRenamedFieldSignature(classIdField, lens.getPrevious())));
+ instructionBuilder.add(new CfInstanceFieldWrite(lens.getNextFieldSignature(classIdField)));
maxStack.set(2);
}
@@ -123,7 +121,8 @@
instructionBuilder.add(new CfLoad(ValueType.OBJECT, 0));
// Load constructor arguments.
- MethodLookupResult parentConstructorLookup = lens.lookupInvokeDirect(parentConstructor, method);
+ MethodLookupResult parentConstructorLookup =
+ lens.lookupInvokeDirect(parentConstructor, method, appView.codeLens());
int i = 0;
for (InstanceFieldInitializationInfo initializationInfo : parentConstructorArguments) {
@@ -195,7 +194,7 @@
int stackSizeForInitializationInfo =
addCfInstructionsForInitializationInfo(
instructionBuilder, initializationInfo, argumentToLocalIndex, field.getType());
- DexField rewrittenField = lens.getRenamedFieldSignature(field, lens.getPrevious());
+ DexField rewrittenField = lens.getNextFieldSignature(field);
// Insert a check to ensure the program continues to type check according to Java type
// checking. Otherwise, instance initializer merging may cause open interfaces. If
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java
index 6f6a957..6698a71 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java
@@ -141,7 +141,7 @@
lens.lookupInvokeSuper(superMethod.getReboundReference(), method).getReference();
fallthroughTarget =
reboundFallthroughTarget.withHolder(
- lens.lookupClassType(superMethod.getReference().getHolderType()),
+ lens.getNextClassType(superMethod.getReference().getHolderType()),
appView.dexItemFactory());
}
instructions.add(
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java
index 29db86f..67022ed 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
@@ -73,6 +74,7 @@
InstanceInitializerDescription.Builder builder =
InstanceInitializerDescription.builder(appView, instanceInitializer);
IRCode code = codeProvider.buildIR(instanceInitializer);
+ GraphLens codeLens = instanceInitializer.getDefinition().getCode().getCodeLens(appView);
WorkList<BasicBlock> workList = WorkList.newIdentityWorkList(code.entryBlock());
while (workList.hasNext()) {
BasicBlock block = workList.next();
@@ -105,7 +107,7 @@
// Check that this writes a field on the enclosing class.
DexField fieldReference = instancePut.getField();
DexField lensRewrittenFieldReference =
- appView.graphLens().lookupField(fieldReference);
+ appView.graphLens().lookupField(fieldReference, codeLens);
if (lensRewrittenFieldReference.getHolderType()
!= instanceInitializer.getHolderType()) {
return invalid();
@@ -143,7 +145,7 @@
DexMethod lensRewrittenInvokedMethod =
appView
.graphLens()
- .lookupInvokeDirect(invokedMethod, instanceInitializer)
+ .lookupInvokeDirect(invokedMethod, instanceInitializer, codeLens)
.getReference();
// TODO(b/189296638): Consider allowing constructor forwarding.
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
index 38b47fc..54f4898 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.dex.Constants.TEMPORARY_INSTANCE_INITIALIZER_PREFIX;
import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.classmerging.SyntheticArgumentClass;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
index 8b4cbce..e87faba 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
@@ -130,7 +130,7 @@
// All the code has been processed so the rewriting required by the lenses is done everywhere,
// we clear lens code rewriting so that the lens rewriter can be re-executed in phase 2 if new
// lenses with code rewriting are added.
- appView.clearCodeRewritings();
+ appView.clearCodeRewritings(executorService);
// Commit synthetics from the primary optimization pass.
commitPendingSyntheticItems(appView);
@@ -215,7 +215,7 @@
// All the code that should be impacted by the lenses inserted between phase 1 and phase 2
// have now been processed and rewritten, we clear code lens rewriting so that the class
// staticizer and phase 3 does not perform again the rewriting.
- appView.clearCodeRewritings();
+ appView.clearCodeRewritings(executorService);
// Commit synthetics before creating a builder (otherwise the builder will not include the
// synthetics.)
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
index 2a3da47..2107f3d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -53,10 +53,6 @@
return graphLens;
}
- public void disallowStaticInterfaceMethodCalls() {
- allowStaticInterfaceMethodCalls = false;
- }
-
public ConstraintWithTarget forAlwaysMaterializingUser() {
return ConstraintWithTarget.ALWAYS;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 7935419..cb5bbbe 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -515,16 +515,17 @@
inliner.performForcedInlining(
method, code, methodCallsOnInstance, inliningIRProvider, methodProcessor, Timing.empty());
} else {
- assert indirectMethodCallsOnInstance.stream()
- .filter(method -> method.getDefinition().getOptimizationInfo().mayHaveSideEffects())
- .allMatch(
- method ->
- method.getDefinition().isInstanceInitializer()
- && !method
- .getDefinition()
- .getOptimizationInfo()
- .getContextInsensitiveInstanceInitializerInfo()
- .mayHaveOtherSideEffectsThanInstanceFieldAssignments());
+ // TODO(b/315284776): Diagnose if this should be removed or reenabled.
+ /*assert indirectMethodCallsOnInstance.stream()
+ .filter(method -> method.getDefinition().getOptimizationInfo().mayHaveSideEffects())
+ .allMatch(
+ method ->
+ method.getDefinition().isInstanceInitializer()
+ && !method
+ .getDefinition()
+ .getOptimizationInfo()
+ .getContextInsensitiveInstanceInitializerInfo()
+ .mayHaveOtherSideEffectsThanInstanceFieldAssignments());*/
}
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
index 37b6314..1eee403 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -135,7 +135,31 @@
}
@Override
- @SuppressWarnings("ReferenceEquality")
+ protected MethodLookupResult internalLookupMethod(
+ DexMethod reference,
+ DexMethod context,
+ InvokeType type,
+ GraphLens codeLens,
+ LookupMethodContinuation continuation) {
+ if (this == codeLens) {
+ // We sometimes create code objects that have the EnumUnboxingLens as code lens.
+ // When using this lens as a code lens there is no lens that will insert the rebound reference
+ // since the MemberRebindingIdentityLens is an ancestor of the EnumUnboxingLens.
+ // We therefore use the reference itself as the rebound reference here, which is safe since
+ // the code objects created during enum unboxing are guaranteed not to contain any non-rebound
+ // method references.
+ MethodLookupResult lookupResult =
+ MethodLookupResult.builder(this, codeLens)
+ .setReboundReference(reference)
+ .setReference(reference)
+ .setType(type)
+ .build();
+ return continuation.lookupMethod(lookupResult);
+ }
+ return super.internalLookupMethod(reference, context, type, codeLens, continuation);
+ }
+
+ @Override
public MethodLookupResult internalDescribeLookupMethod(
MethodLookupResult previous, DexMethod context, GraphLens codeLens) {
assert context != null || verifyIsContextFreeForMethod(previous.getReference(), codeLens);
@@ -146,9 +170,9 @@
DexMethod previousContext = getPreviousMethodSignature(context);
DexType superEnum = unboxedEnums.representativeType(previousContext.getHolderType());
if (unboxedEnums.isUnboxedEnum(superEnum)) {
- if (superEnum != previousContext.getHolderType()) {
+ if (superEnum.isNotIdenticalTo(previousContext.getHolderType())) {
DexMethod reference = previous.getReference();
- if (reference.getHolderType() != superEnum) {
+ if (reference.getHolderType().isNotIdenticalTo(superEnum)) {
// We are in an enum subtype where super-invokes are rebound differently.
reference = reference.withHolder(superEnum, dexItemFactory());
}
@@ -156,7 +180,7 @@
} else {
// This is a super-invoke to a library method, not rewritten by the lens.
// This is rewritten by the EnumUnboxerRewriter.
- return previous;
+ return previous.verify(this, codeLens);
}
} else {
result = methodMap.apply(previous.getReference());
@@ -165,12 +189,13 @@
result = methodMap.apply(previous.getReference());
}
if (result == null) {
- return previous;
+ return previous.verify(this, codeLens);
}
- return MethodLookupResult.builder(this)
+ return MethodLookupResult.builder(this, codeLens)
.setReference(result)
.setPrototypeChanges(
- internalDescribePrototypeChanges(previous.getPrototypeChanges(), result))
+ internalDescribePrototypeChanges(
+ previous.getPrototypeChanges(), previous.getReference(), result))
.setType(mapInvocationType(result, previous.getReference(), previous.getType()))
.build();
}
@@ -178,7 +203,9 @@
@Override
@SuppressWarnings("ReferenceEquality")
protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
- RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
+ RewrittenPrototypeDescription prototypeChanges,
+ DexMethod previousMethod,
+ DexMethod newMethod) {
// Rewrite the single value of the given RewrittenPrototypeDescription if it is referring to an
// unboxed enum field.
if (prototypeChanges.hasRewrittenReturnInfo()) {
@@ -198,12 +225,8 @@
}
}
}
-
- // During the second IR processing enum unboxing is the only optimization rewriting
- // prototype description, if this does not hold, remove the assertion and merge
- // the two prototype changes.
RewrittenPrototypeDescription enumUnboxingPrototypeChanges =
- prototypeChangesPerMethod.getOrDefault(method, RewrittenPrototypeDescription.none());
+ prototypeChangesPerMethod.getOrDefault(newMethod, RewrittenPrototypeDescription.none());
return prototypeChanges.combine(enumUnboxingPrototypeChanges);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/numberunboxer/NumberUnboxerLens.java b/src/main/java/com/android/tools/r8/ir/optimize/numberunboxer/NumberUnboxerLens.java
index eeb1768..8e7c278 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/numberunboxer/NumberUnboxerLens.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/numberunboxer/NumberUnboxerLens.java
@@ -35,10 +35,12 @@
@Override
protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
- RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
- RewrittenPrototypeDescription enumUnboxingPrototypeChanges =
- prototypeChangesPerMethod.getOrDefault(method, RewrittenPrototypeDescription.none());
- return prototypeChanges.combine(enumUnboxingPrototypeChanges);
+ RewrittenPrototypeDescription previousPrototypeChanges,
+ DexMethod previousMethod,
+ DexMethod newMethod) {
+ RewrittenPrototypeDescription prototypeChanges =
+ prototypeChangesPerMethod.getOrDefault(newMethod, RewrittenPrototypeDescription.none());
+ return previousPrototypeChanges.combine(prototypeChanges);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
deleted file mode 100644
index 4b71a32..0000000
--- a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (c) 2018, 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.optimize;
-
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.lens.DefaultNonIdentityGraphLens;
-import com.android.tools.r8.graph.lens.FieldLookupResult;
-import com.android.tools.r8.graph.lens.GraphLens;
-import java.util.IdentityHashMap;
-import java.util.Map;
-
-/**
- * This lens is used to populate the rebound field reference during lookup, such that both the
- * non-rebound and rebound field references are available to all descendants of this lens.
- *
- * <p>TODO(b/157616970): All uses of this should be replaced by {@link MemberRebindingIdentityLens}.
- */
-public class FieldRebindingIdentityLens extends DefaultNonIdentityGraphLens {
-
- private final Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap;
-
- private FieldRebindingIdentityLens(
- Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap,
- AppView<? extends AppInfoWithClassHierarchy> appView,
- GraphLens previousLens) {
- super(appView, previousLens);
- this.nonReboundFieldReferenceToDefinitionMap = nonReboundFieldReferenceToDefinitionMap;
- }
-
- public static Builder builder() {
- return new Builder();
- }
-
- @Override
- public boolean hasCodeRewritings() {
- return false;
- }
-
- @Override
- protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
- assert !previous.hasReadCastType();
- assert !previous.hasReboundReference();
- return FieldLookupResult.builder(this)
- .setReference(previous.getReference())
- .setReboundReference(getReboundFieldReference(previous.getReference()))
- .build();
- }
-
- private DexField getReboundFieldReference(DexField field) {
- return nonReboundFieldReferenceToDefinitionMap.getOrDefault(field, field);
- }
-
- public static class Builder {
-
- private final Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap =
- new IdentityHashMap<>();
-
- private Builder() {}
-
- void recordDefinitionForNonReboundFieldReference(
- DexField nonReboundFieldReference, DexField reboundFieldReference) {
- nonReboundFieldReferenceToDefinitionMap.put(nonReboundFieldReference, reboundFieldReference);
- }
-
- FieldRebindingIdentityLens build(AppView<? extends AppInfoWithClassHierarchy> appView) {
- // This intentionally does not return null when the map is empty. In this case there are no
- // non-rebound field references, but the member rebinding lens is still needed to populate the
- // rebound reference during field lookup.
- return new FieldRebindingIdentityLens(
- nonReboundFieldReferenceToDefinitionMap, appView, GraphLens.getIdentityLens());
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index b608f37..5acd3f2 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -16,13 +16,11 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.FieldAccessInfoCollection;
import com.android.tools.r8.graph.LibraryMethod;
import com.android.tools.r8.graph.MethodAccessInfoCollection;
import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.code.InvokeType;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -30,8 +28,6 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.Pair;
-import com.android.tools.r8.utils.SetUtils;
-import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.TriConsumer;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.Iterables;
@@ -40,9 +36,7 @@
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -64,10 +58,6 @@
this.lensBuilder = MemberRebindingLens.builder(appView);
}
- private AppView<AppInfoWithLiveness> appView() {
- return appView;
- }
-
private DexMethod validMemberRebindingTargetForNonProgramMethod(
DexClassAndMethod resolvedMethod,
SingleResolutionResult<?> resolutionResult,
@@ -515,141 +505,13 @@
return null;
}
- private void recordNonReboundFieldAccesses(ExecutorService executorService)
- throws ExecutionException {
- assert verifyFieldAccessCollectionContainsAllNonReboundFieldReferences(executorService);
- FieldAccessInfoCollection<?> fieldAccessInfoCollection =
- appView.appInfo().getFieldAccessInfoCollection();
- fieldAccessInfoCollection.forEach(lensBuilder::recordNonReboundFieldAccesses);
- }
-
- public void run(ExecutorService executorService) throws ExecutionException {
+ public void run() throws ExecutionException {
AppInfoWithLiveness appInfo = appView.appInfo();
computeMethodRebinding(appInfo.getMethodAccessInfoCollection());
- recordNonReboundFieldAccesses(executorService);
appInfo.getFieldAccessInfoCollection().flattenAccessContexts();
MemberRebindingLens memberRebindingLens = lensBuilder.build();
appView.setGraphLens(memberRebindingLens);
eventConsumer.finished(appView, memberRebindingLens);
appView.notifyOptimizationFinishedForTesting();
}
-
- @SuppressWarnings("ReferenceEquality")
- private boolean verifyFieldAccessCollectionContainsAllNonReboundFieldReferences(
- ExecutorService executorService) throws ExecutionException {
- Set<DexField> nonReboundFieldReferences = computeNonReboundFieldReferences(executorService);
- FieldAccessInfoCollection<?> fieldAccessInfoCollection =
- appView.appInfo().getFieldAccessInfoCollection();
- fieldAccessInfoCollection.forEach(
- info -> {
- DexField reboundFieldReference = info.getField();
- info.forEachIndirectAccess(
- nonReboundFieldReference -> {
- assert reboundFieldReference != nonReboundFieldReference;
- assert reboundFieldReference
- == appView
- .appInfo()
- .resolveField(nonReboundFieldReference)
- .getResolvedFieldReference();
- nonReboundFieldReferences.remove(nonReboundFieldReference);
- });
- });
- assert nonReboundFieldReferences.isEmpty();
- return true;
- }
-
- private Set<DexField> computeNonReboundFieldReferences(ExecutorService executorService)
- throws ExecutionException {
- Set<DexField> nonReboundFieldReferences = SetUtils.newConcurrentHashSet();
- ThreadUtils.processItems(
- appView.appInfo()::forEachMethod,
- method -> {
- if (method.getDefinition().hasCode()) {
- method.registerCodeReferences(
- new UseRegistry<ProgramMethod>(appView, method) {
-
- @Override
- public void registerInstanceFieldRead(DexField field) {
- registerFieldReference(field);
- }
-
- @Override
- public void registerInstanceFieldWrite(DexField field) {
- registerFieldReference(field);
- }
-
- @Override
- public void registerStaticFieldRead(DexField field) {
- registerFieldReference(field);
- }
-
- @Override
- public void registerStaticFieldWrite(DexField field) {
- registerFieldReference(field);
- }
-
- @SuppressWarnings("ReferenceEquality")
- private void registerFieldReference(DexField field) {
- appView()
- .appInfo()
- .resolveField(field)
- .forEachSuccessfulFieldResolutionResult(
- resolutionResult -> {
- if (resolutionResult.getResolvedField().getReference() != field) {
- nonReboundFieldReferences.add(field);
- }
- });
- }
-
- @Override
- public void registerInitClass(DexType type) {
- // Intentionally empty.
- }
-
- @Override
- public void registerInvokeDirect(DexMethod method) {
- // Intentionally empty.
- }
-
- @Override
- public void registerInvokeInterface(DexMethod method) {
- // Intentionally empty.
- }
-
- @Override
- public void registerInvokeStatic(DexMethod method) {
- // Intentionally empty.
- }
-
- @Override
- public void registerInvokeSuper(DexMethod method) {
- // Intentionally empty.
- }
-
- @Override
- public void registerInvokeVirtual(DexMethod method) {
- // Intentionally empty.
- }
-
- @Override
- public void registerNewInstance(DexType type) {
- // Intentionally empty.
- }
-
- @Override
- public void registerInstanceOf(DexType type) {
- // Intentionally empty.
- }
-
- @Override
- public void registerTypeReference(DexType type) {
- // Intentionally empty.
- }
- });
- }
- },
- appView.options().getThreadingModule(),
- executorService);
- return nonReboundFieldReferences;
- }
}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
index 60d5c62..234b353 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
@@ -77,7 +77,7 @@
public MethodLookupResult internalDescribeLookupMethod(
MethodLookupResult previous, DexMethod context, GraphLens codeLens) {
assert previous.getReboundReference() == null;
- return MethodLookupResult.builder(this)
+ return MethodLookupResult.builder(this, codeLens)
.setReference(previous.getReference())
.setReboundReference(getReboundMethodReference(previous.getReference()))
.setPrototypeChanges(previous.getPrototypeChanges())
@@ -85,10 +85,9 @@
.build();
}
- @SuppressWarnings("ReferenceEquality")
private DexMethod getReboundMethodReference(DexMethod method) {
DexMethod rebound = nonReboundMethodReferenceToDefinitionMap.get(method);
- assert method != rebound;
+ assert method.isNotIdenticalTo(rebound);
while (rebound != null) {
method = rebound;
rebound = nonReboundMethodReferenceToDefinitionMap.get(method);
@@ -130,8 +129,10 @@
lens.lookupType(
nonReboundFieldReference.getHolderType(), appliedMemberRebindingLens),
dexItemFactory);
- builder.recordNonReboundFieldAccess(
- rewrittenNonReboundFieldReference, rewrittenReboundFieldReference);
+ if (rewrittenNonReboundFieldReference.isNotIdenticalTo(rewrittenReboundFieldReference)) {
+ builder.recordNonReboundFieldAccess(
+ rewrittenNonReboundFieldReference, rewrittenReboundFieldReference);
+ }
});
nonReboundMethodReferenceToDefinitionMap.forEach(
(nonReboundMethodReference, reboundMethodReference) -> {
@@ -142,8 +143,11 @@
lens.lookupType(
nonReboundMethodReference.getHolderType(), appliedMemberRebindingLens),
dexItemFactory);
- builder.recordNonReboundMethodAccess(
- rewrittenNonReboundMethodReference, rewrittenReboundMethodReference);
+ if (rewrittenNonReboundMethodReference.isNotIdenticalTo(
+ rewrittenReboundMethodReference)) {
+ builder.recordNonReboundMethodAccess(
+ rewrittenNonReboundMethodReference, rewrittenReboundMethodReference);
+ }
});
return builder.build();
}
@@ -171,11 +175,13 @@
private void recordNonReboundFieldAccess(
DexField nonReboundFieldReference, DexField reboundFieldReference) {
+ assert nonReboundFieldReference.isNotIdenticalTo(reboundFieldReference);
nonReboundFieldReferenceToDefinitionMap.put(nonReboundFieldReference, reboundFieldReference);
}
private void recordNonReboundMethodAccess(
DexMethod nonReboundMethodReference, DexMethod reboundMethodReference) {
+ assert nonReboundMethodReference.isNotIdenticalTo(reboundMethodReference);
nonReboundMethodReferenceToDefinitionMap.put(
nonReboundMethodReference, reboundMethodReference);
}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
index 331d29e..19e25d2 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
@@ -37,6 +37,30 @@
* Otherwise we apply the {@link NonReboundMemberReferencesRegistry} below to all code objects to
* compute the mapping.
*/
+ public static MemberRebindingIdentityLens rebuild(
+ AppView<? extends AppInfoWithClassHierarchy> appView, ExecutorService executorService)
+ throws ExecutionException {
+ FieldAccessInfoCollectionImpl mutableFieldAccessInfoCollection =
+ new FieldAccessInfoCollectionImpl(new ConcurrentHashMap<>());
+ MethodAccessInfoCollection.ConcurrentBuilder methodAccessInfoCollectionBuilder =
+ MethodAccessInfoCollection.concurrentBuilder();
+ initializeMemberAccessInfoCollectionsForMemberRebinding(
+ appView,
+ mutableFieldAccessInfoCollection,
+ methodAccessInfoCollectionBuilder,
+ executorService);
+ return create(
+ appView, mutableFieldAccessInfoCollection, methodAccessInfoCollectionBuilder.build());
+ }
+
+ /**
+ * In order to construct an instance of {@link MemberRebindingIdentityLens} we need a mapping from
+ * non-rebound field and method references to their definitions.
+ *
+ * <p>If shrinking or minification is enabled, we retrieve these from {@link AppInfoWithLiveness}.
+ * Otherwise we apply the {@link NonReboundMemberReferencesRegistry} below to all code objects to
+ * compute the mapping.
+ */
public static MemberRebindingIdentityLens create(
AppView<? extends AppInfoWithClassHierarchy> appView, ExecutorService executorService)
throws ExecutionException {
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
index 6b2903e..6445afd 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
@@ -6,17 +6,12 @@
import static com.android.tools.r8.graph.lens.NestedGraphLens.mapVirtualInterfaceInvocationTypes;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.FieldAccessInfo;
import com.android.tools.r8.graph.lens.DefaultNonIdentityGraphLens;
import com.android.tools.r8.graph.lens.FieldLookupResult;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.graph.lens.MethodLookupResult;
-import com.android.tools.r8.graph.lens.NonIdentityGraphLens;
import com.android.tools.r8.ir.code.InvokeType;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Collections;
@@ -26,15 +21,11 @@
public class MemberRebindingLens extends DefaultNonIdentityGraphLens {
private final Map<InvokeType, Map<DexMethod, DexMethod>> methodMaps;
- private final Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap;
public MemberRebindingLens(
- AppView<AppInfoWithLiveness> appView,
- Map<InvokeType, Map<DexMethod, DexMethod>> methodMaps,
- Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap) {
+ AppView<AppInfoWithLiveness> appView, Map<InvokeType, Map<DexMethod, DexMethod>> methodMaps) {
super(appView);
this.methodMaps = methodMaps;
- this.nonReboundFieldReferenceToDefinitionMap = nonReboundFieldReferenceToDefinitionMap;
}
public static Builder builder(AppView<AppInfoWithLiveness> appView) {
@@ -53,16 +44,7 @@
@Override
protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
- assert !previous.hasReadCastType();
- assert !previous.hasReboundReference();
- return FieldLookupResult.builder(this)
- .setReference(previous.getReference())
- .setReboundReference(getReboundFieldReference(previous.getReference()))
- .build();
- }
-
- private DexField getReboundFieldReference(DexField field) {
- return nonReboundFieldReferenceToDefinitionMap.getOrDefault(field, field);
+ return previous;
}
@Override
@@ -70,16 +52,15 @@
MethodLookupResult previous, DexMethod context, GraphLens codeLens) {
Map<DexMethod, DexMethod> methodMap =
methodMaps.getOrDefault(previous.getType(), Collections.emptyMap());
- DexMethod newMethod = methodMap.get(previous.getReference());
- if (newMethod == null) {
- return previous;
- }
- return MethodLookupResult.builder(this)
- .setReference(newMethod)
+ DexMethod newReboundMethod =
+ methodMap.getOrDefault(previous.getReference(), previous.getReference());
+ return MethodLookupResult.builder(this, codeLens)
+ .setReboundReference(newReboundMethod)
+ .setReference(newReboundMethod)
.setPrototypeChanges(previous.getPrototypeChanges())
.setType(
mapVirtualInterfaceInvocationTypes(
- appView, newMethod, previous.getReference(), previous.getType()))
+ appView, newReboundMethod, previous.getReference(), previous.getType()))
.build();
}
@@ -91,33 +72,10 @@
return getPrevious().isIdentityLensForFields(codeLens);
}
- public FieldRebindingIdentityLens toRewrittenFieldRebindingLens(
- AppView<? extends AppInfoWithClassHierarchy> appView,
- GraphLens lens,
- NonIdentityGraphLens appliedMemberRebindingLens) {
- DexItemFactory dexItemFactory = appView.dexItemFactory();
- FieldRebindingIdentityLens.Builder builder = FieldRebindingIdentityLens.builder();
- nonReboundFieldReferenceToDefinitionMap.forEach(
- (nonReboundFieldReference, reboundFieldReference) -> {
- DexField rewrittenReboundFieldReference =
- lens.getRenamedFieldSignature(reboundFieldReference, appliedMemberRebindingLens);
- DexField rewrittenNonReboundFieldReference =
- rewrittenReboundFieldReference.withHolder(
- lens.lookupType(
- nonReboundFieldReference.getHolderType(), appliedMemberRebindingLens),
- dexItemFactory);
- builder.recordDefinitionForNonReboundFieldReference(
- rewrittenNonReboundFieldReference, rewrittenReboundFieldReference);
- });
- return builder.build(appView);
- }
-
public static class Builder {
private final AppView<AppInfoWithLiveness> appView;
private final Map<InvokeType, Map<DexMethod, DexMethod>> methodMaps = new IdentityHashMap<>();
- private final Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap =
- new IdentityHashMap<>();
private Builder(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
@@ -135,20 +93,8 @@
methodMap.put(from, to);
}
- void recordNonReboundFieldAccesses(FieldAccessInfo info) {
- DexField reboundFieldReference = info.getField();
- info.forEachIndirectAccess(
- nonReboundFieldReference ->
- recordNonReboundFieldAccess(nonReboundFieldReference, reboundFieldReference));
- }
-
- private void recordNonReboundFieldAccess(
- DexField nonReboundFieldReference, DexField reboundFieldReference) {
- nonReboundFieldReferenceToDefinitionMap.put(nonReboundFieldReference, reboundFieldReference);
- }
-
public MemberRebindingLens build() {
- return new MemberRebindingLens(appView, methodMaps, nonReboundFieldReferenceToDefinitionMap);
+ return new MemberRebindingLens(appView, methodMaps);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/accessmodification/AccessModifierLens.java b/src/main/java/com/android/tools/r8/optimize/accessmodification/AccessModifierLens.java
index 8a649e1..81edcbf 100644
--- a/src/main/java/com/android/tools/r8/optimize/accessmodification/AccessModifierLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/accessmodification/AccessModifierLens.java
@@ -56,27 +56,34 @@
}
@Override
- @SuppressWarnings("ReferenceEquality")
public MethodLookupResult internalDescribeLookupMethod(
MethodLookupResult previous, DexMethod context, GraphLens codeLens) {
- assert !previous.hasReboundReference();
- DexMethod newMethod = getNextMethodSignature(previous.getReference());
+ DexMethod newReboundReference = getNextMethodSignature(previous.getReboundReference());
+ assert newReboundReference
+ .getHolderType()
+ .isIdenticalTo(previous.getReboundReference().getHolderType());
+
InvokeType newInvokeType = previous.getType();
- if (previous.getType() == InvokeType.DIRECT) {
- if (publicizedPrivateInterfaceMethods.contains(newMethod)) {
+ if (previous.getType().isDirect()) {
+ if (publicizedPrivateInterfaceMethods.contains(newReboundReference)) {
newInvokeType = InvokeType.INTERFACE;
- } else if (publicizedPrivateVirtualMethods.contains(newMethod)) {
+ } else if (publicizedPrivateVirtualMethods.contains(newReboundReference)) {
newInvokeType = InvokeType.VIRTUAL;
}
}
- if (newInvokeType != previous.getType() || newMethod != previous.getReference()) {
- return MethodLookupResult.builder(this)
- .setReference(newMethod)
+
+ if (newInvokeType != previous.getType()
+ || newReboundReference.isNotIdenticalTo(previous.getReboundReference())) {
+ DexMethod newReference =
+ newReboundReference.withHolder(previous.getReference(), dexItemFactory());
+ return MethodLookupResult.builder(this, codeLens)
+ .setReboundReference(newReboundReference)
+ .setReference(newReference)
.setPrototypeChanges(previous.getPrototypeChanges())
.setType(newInvokeType)
.build();
}
- return previous;
+ return previous.verify(this, codeLens);
}
public static class Builder {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
index d20c06b..a5a4a61 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
@@ -57,10 +57,9 @@
}
@Override
- @SuppressWarnings("ReferenceEquality")
protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
FieldLookupResult lookupResult = super.internalDescribeLookupField(previous);
- if (lookupResult.getReference().getType() != previous.getReference().getType()) {
+ if (lookupResult.getReference().getType().isNotIdenticalTo(previous.getReference().getType())) {
return FieldLookupResult.builder(this)
.setReboundReference(lookupResult.getReboundReference())
.setReference(lookupResult.getReference())
@@ -73,15 +72,17 @@
@Override
protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
- RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
- DexMethod previous = getPreviousMethodSignature(method);
- if (!hasPrototypeChanges(method)) {
+ RewrittenPrototypeDescription prototypeChanges,
+ DexMethod previousMethod,
+ DexMethod newMethod) {
+ DexMethod previous = getPreviousMethodSignature(newMethod);
+ if (!hasPrototypeChanges(newMethod)) {
return prototypeChanges;
}
RewrittenPrototypeDescription newPrototypeChanges =
- prototypeChanges.combine(getPrototypeChanges(method));
+ prototypeChanges.combine(getPrototypeChanges(newMethod));
assert previous.getReturnType().isVoidType()
- || !method.getReturnType().isVoidType()
+ || !newMethod.getReturnType().isVoidType()
|| newPrototypeChanges.hasRewrittenReturnInfo();
return newPrototypeChanges;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
index 6e8a445..166b08e 100644
--- a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
@@ -10,7 +10,7 @@
import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
import java.util.Set;
-class BridgeHoistingLens extends DefaultNonIdentityGraphLens {
+public class BridgeHoistingLens extends DefaultNonIdentityGraphLens {
// Mapping from non-hoisted bridge methods to hoisted bridge methods.
private final BidirectionalManyToOneMap<DexMethod, DexMethod> bridgeToHoistedBridgeMap;
diff --git a/src/main/java/com/android/tools/r8/optimize/compose/ComposableCallGraph.java b/src/main/java/com/android/tools/r8/optimize/compose/ComposableCallGraph.java
index 0cbf138..8364488 100644
--- a/src/main/java/com/android/tools/r8/optimize/compose/ComposableCallGraph.java
+++ b/src/main/java/com/android/tools/r8/optimize/compose/ComposableCallGraph.java
@@ -87,7 +87,8 @@
// TODO(b/302483644): Parallelize identification of @Composable call sites.
private void addCallEdgesToComposableFunctions() {
// Code is fully rewritten so no need to lens rewrite in registry.
- assert appView.codeLens() == appView.graphLens();
+ assert appView.graphLens().isMemberRebindingIdentityLens();
+ assert appView.codeLens() == appView.graphLens().asNonIdentityLens().getPrevious();
for (DexProgramClass clazz : appView.appInfo().classes()) {
clazz.forEachProgramMethodMatching(
diff --git a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java
index 74e3046..dd3f5bd 100644
--- a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizerGraphLens.java
@@ -58,24 +58,28 @@
}
@Override
- @SuppressWarnings("ReferenceEquality")
protected MethodLookupResult internalDescribeLookupMethod(
MethodLookupResult previous, DexMethod context, GraphLens codeLens) {
- DexMethod methodSignature = previous.getReference();
- DexMethod newMethodSignature = getNextMethodSignature(methodSignature);
- if (methodSignature == newMethodSignature) {
- return previous;
+ assert previous.hasReboundReference();
+ DexMethod previousReboundReference = previous.getReboundReference();
+ DexMethod newReboundReference = getNextMethodSignature(previousReboundReference);
+ if (newReboundReference.isIdenticalTo(previousReboundReference)) {
+ return previous.verify(this, codeLens);
}
- assert !previous.hasReboundReference()
- || previous.getReference() == previous.getReboundReference();
- return MethodLookupResult.builder(this)
+ DexMethod previousReference = previous.getReference();
+ DexMethod newReference =
+ previousReference.isIdenticalTo(previousReboundReference)
+ ? newReboundReference
+ : newReboundReference.withHolder(previousReference.getHolderType(), dexItemFactory());
+ return MethodLookupResult.builder(this, codeLens)
.setPrototypeChanges(
previous
.getPrototypeChanges()
.combine(
prototypeChanges.getOrDefault(
- newMethodSignature, RewrittenPrototypeDescription.none())))
- .setReference(newMethodSignature)
+ newReboundReference, RewrittenPrototypeDescription.none())))
+ .setReboundReference(newReboundReference)
+ .setReference(newReference)
.setType(previous.getType())
.build();
}
diff --git a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalLens.java b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalLens.java
index 9de23a8..5e40a11 100644
--- a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalLens.java
@@ -47,11 +47,16 @@
} while (methodMap.containsKey(newReference));
boolean holderTypeIsInterface = interfaces.contains(newReference.getHolderType());
if (previous.getType().isSuper() && holderTypeIsInterface) {
- return previous;
+ return MethodLookupResult.builder(this, codeLens)
+ .setReboundReference(newReference)
+ .setReference(previous.getReference())
+ .setPrototypeChanges(previous.getPrototypeChanges())
+ .setType(previous.getType())
+ .build();
}
- return MethodLookupResult.builder(this)
- .setReference(newReference)
+ return MethodLookupResult.builder(this, codeLens)
.setReboundReference(newReference)
+ .setReference(newReference)
.setPrototypeChanges(previous.getPrototypeChanges())
.setType(
holderTypeIsInterface && previous.getType().isVirtual()
@@ -59,7 +64,7 @@
: previous.getType())
.build();
}
- return previous;
+ return previous.verify(this, codeLens);
}
public static class Builder {
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/verticalclassmerging/ClassMerger.java
index 5fb872d..166b1b1 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/ClassMerger.java
@@ -39,8 +39,6 @@
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.graph.lens.MethodLookupResult;
-import com.android.tools.r8.ir.code.InvokeType;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -94,7 +92,7 @@
DexProgramClass source,
DexProgramClass target) {
this.appView = appView;
- this.deferredRenamings = new VerticalClassMergerGraphLens.Builder(appView);
+ this.deferredRenamings = new VerticalClassMergerGraphLens.Builder();
this.dexItemFactory = appView.dexItemFactory();
this.lensBuilder = lensBuilder;
this.verticallyMergedClassesBuilder = verticallyMergedClassesBuilder;
@@ -131,7 +129,7 @@
availableMethodSignatures.test(candidate)
&& source.lookupVirtualMethod(candidate) == null);
add(directMethods, resultingConstructor, MethodSignatureEquivalence.get());
- blockRedirectionOfSuperCalls(resultingConstructor.getReference());
+ blockRedirectionOfSuperCalls(resultingConstructor);
} else {
DexEncodedMethod resultingDirectMethod =
renameMethod(
@@ -139,11 +137,8 @@
availableMethodSignatures,
definition.isClassInitializer() ? Rename.NEVER : Rename.IF_NEEDED);
add(directMethods, resultingDirectMethod, MethodSignatureEquivalence.get());
- deferredRenamings.map(
- directMethod.getReference(), resultingDirectMethod.getReference());
- deferredRenamings.recordMove(
- directMethod.getReference(), resultingDirectMethod.getReference());
- blockRedirectionOfSuperCalls(resultingDirectMethod.getReference());
+ deferredRenamings.recordMove(directMethod.getDefinition(), resultingDirectMethod);
+ blockRedirectionOfSuperCalls(resultingDirectMethod);
// Private methods in the parent class may be targeted with invoke-super if the two
// classes are in the same nest. Ensure such calls are mapped to invoke-direct.
@@ -151,12 +146,10 @@
&& definition.isPrivate()
&& AccessControl.isMemberAccessible(directMethod, source, target, appView)
.isTrue()) {
- deferredRenamings.mapVirtualMethodToDirectInType(
- directMethod.getReference(),
- prototypeChanges ->
- new MethodLookupResult(
- resultingDirectMethod.getReference(), null, DIRECT, prototypeChanges),
- target.getType());
+ // TODO(b/315283465): Add a test for correct rewriting of invoke-super to nest members
+ // and determine if we need to record something here or not.
+ // deferredRenamings.mapVirtualMethodToDirectInType(
+ // directMethod.getReference(), target.getType());
}
}
});
@@ -168,15 +161,12 @@
// Remove abstract/interface methods that are shadowed. The identity mapping below is
// needed to ensure we correctly fixup the mapping in case the signature refers to
// merged classes.
- deferredRenamings
- .map(virtualMethod.getReference(), shadowedBy.getReference())
- .map(shadowedBy.getReference(), shadowedBy.getReference())
- .recordMerge(virtualMethod.getReference(), shadowedBy.getReference());
+ deferredRenamings.recordSplit(virtualMethod, shadowedBy, null, null);
// The override now corresponds to the method in the parent, so unset its synthetic flag
// if the method in the parent is not synthetic.
if (!virtualMethod.isSyntheticMethod() && shadowedBy.isSyntheticMethod()) {
- shadowedBy.accessFlags.demoteFromSynthetic();
+ shadowedBy.getAccessFlags().demoteFromSynthetic();
}
continue;
}
@@ -202,17 +192,14 @@
DexEncodedMethod resultingVirtualMethod =
renameMethod(virtualMethod, availableMethodSignatures, Rename.NEVER);
resultingVirtualMethod.setLibraryMethodOverride(virtualMethod.isLibraryMethodOverride());
- deferredRenamings.map(
- virtualMethod.getReference(), resultingVirtualMethod.getReference());
- deferredRenamings.recordMove(
- virtualMethod.getReference(), resultingVirtualMethod.getReference());
+ deferredRenamings.recordMove(virtualMethod, resultingVirtualMethod);
add(virtualMethods, resultingVirtualMethod, MethodSignatureEquivalence.get());
continue;
}
}
DexEncodedMethod resultingMethod;
- if (source.accessFlags.isInterface()) {
+ if (source.isInterface()) {
// Moving a default interface method into its subtype. This method could be hit directly
// via an invoke-super instruction from any of the transitive subtypes of this interface,
// due to the way invoke-super works on default interface methods. In order to be able
@@ -231,16 +218,12 @@
resultingMethodReference, dexItemFactory);
makeStatic(resultingMethod);
} else {
- // This virtual method could be called directly from a sub class via an invoke-super in-
- // struction. Therefore, we translate this virtual method into an instance method with a
+ // This virtual method could be called directly from a sub class via an invoke-super
+ // instruction. Therefore, we translate this virtual method into an instance method with a
// unique name, such that relevant invoke-super instructions can be rewritten to target
// this method directly.
resultingMethod = renameMethod(virtualMethod, availableMethodSignatures, Rename.ALWAYS);
- if (appView.options().getProguardConfiguration().isAccessModificationAllowed()) {
- makePublic(resultingMethod);
- } else {
- makePrivate(resultingMethod);
- }
+ makePublicFinal(resultingMethod);
}
add(
@@ -250,18 +233,17 @@
// Record that invoke-super instructions in the target class should be redirected to the
// newly created direct method.
- redirectSuperCallsInTarget(virtualMethod, resultingMethod);
- blockRedirectionOfSuperCalls(resultingMethod.getReference());
+ redirectSuperCallsInTarget(virtualMethod);
+ DexEncodedMethod bridge = null;
+ DexEncodedMethod override = shadowedBy;
if (shadowedBy == null) {
// In addition to the newly added direct method, create a virtual method such that we do
// not accidentally remove the method from the interface of this class.
// Note that this method is added independently of whether it will actually be used. If
// it turns out that the method is never used, it will be removed by the final round
// of tree shaking.
- shadowedBy = buildBridgeMethod(virtualMethod, resultingMethod);
- deferredRenamings.recordCreationOfBridgeMethod(
- virtualMethod.getReference(), shadowedBy.getReference());
+ bridge = shadowedBy = buildBridgeMethod(virtualMethod, resultingMethod);
add(virtualMethods, shadowedBy, MethodSignatureEquivalence.get());
}
@@ -279,9 +261,7 @@
.getMethodInfo(virtualMethod, source)
.joiner())));
- deferredRenamings.map(virtualMethod.getReference(), shadowedBy.getReference());
- deferredRenamings.recordMove(
- virtualMethod.getReference(), resultingMethod.getReference(), resultingMethod.isStatic());
+ deferredRenamings.recordSplit(virtualMethod, override, bridge, resultingMethod);
}
if (abortMerge) {
@@ -363,7 +343,12 @@
source.getMethodCollection().clearVirtualMethods();
source.clearInstanceFields();
source.clearStaticFields();
- // Step 5: Record merging.
+ // Step 5: Merge attributes.
+ if (source.isNestHost()) {
+ target.clearNestHost();
+ target.setNestMemberAttributes(source.getNestMembersClassAttributes());
+ }
+ // Step 6: Record merging.
assert !abortMerge;
assert GenericSignatureCorrectnessHelper.createForVerification(
appView, GenericSignatureContextBuilder.createForSingleClass(appView, target))
@@ -535,11 +520,9 @@
return synthesizedBridges;
}
- private void redirectSuperCallsInTarget(DexEncodedMethod oldTarget, DexEncodedMethod newTarget) {
+ private void redirectSuperCallsInTarget(DexEncodedMethod oldTarget) {
DexMethod oldTargetReference = oldTarget.getReference();
- DexMethod newTargetReference = newTarget.getReference();
- InvokeType newTargetType = newTarget.isNonPrivateVirtualMethod() ? VIRTUAL : DIRECT;
- if (source.accessFlags.isInterface()) {
+ if (source.isInterface()) {
// If we merge a default interface method from interface I to its subtype C, then we need
// to rewrite invocations on the form "invoke-super I.m()" to "invoke-direct C.m$I()".
//
@@ -548,10 +531,7 @@
// if I has a supertype J. This is due to the fact that invoke-super instructions that
// resolve to a method on an interface never hit an implementation below that interface.
deferredRenamings.mapVirtualMethodToDirectInType(
- oldTargetReference,
- prototypeChanges ->
- new MethodLookupResult(newTargetReference, null, STATIC, prototypeChanges),
- target.type);
+ oldTargetReference, oldTarget, target.getType());
} else {
// If we merge class B into class C, and class C contains an invocation super.m(), then it
// is insufficient to rewrite "invoke-super B.m()" to "invoke-{direct,virtual} C.m$B()" (the
@@ -562,7 +542,7 @@
//
// We handle this by adding a mapping for [target] and all of its supertypes.
DexProgramClass holder = target;
- while (holder != null && holder.isProgramClass()) {
+ while (holder != null) {
DexMethod signatureInHolder = oldTargetReference.withHolder(holder, dexItemFactory);
// Only rewrite the invoke-super call if it does not lead to a NoSuchMethodError.
boolean resolutionSucceeds =
@@ -570,10 +550,7 @@
|| appView.appInfo().lookupSuperTarget(signatureInHolder, holder, appView) != null;
if (resolutionSucceeds) {
deferredRenamings.mapVirtualMethodToDirectInType(
- signatureInHolder,
- prototypeChanges ->
- new MethodLookupResult(newTargetReference, null, newTargetType, prototypeChanges),
- target.type);
+ signatureInHolder, oldTarget, target.type);
} else {
break;
}
@@ -588,17 +565,15 @@
DexMethod signatureInType = oldTargetReference.withHolder(type, dexItemFactory);
// Resolution would have succeeded if the method used to be in [type], or if one of
// its super classes declared the method.
+ // TODO(b/315283244): Should not rely on lens for this. Instead precompute this before
+ // merging any classes.
boolean resolutionSucceededBeforeMerge =
lensBuilder.hasMappingForSignatureInContext(holder, signatureInType)
|| appView.appInfo().lookupSuperTarget(signatureInHolder, holder, appView)
!= null;
if (resolutionSucceededBeforeMerge) {
deferredRenamings.mapVirtualMethodToDirectInType(
- signatureInType,
- prototypeChanges ->
- new MethodLookupResult(
- newTargetReference, null, newTargetType, prototypeChanges),
- target.type);
+ signatureInType, oldTarget, target.type);
}
}
holder =
@@ -609,7 +584,7 @@
}
}
- private void blockRedirectionOfSuperCalls(DexMethod method) {
+ private void blockRedirectionOfSuperCalls(DexEncodedMethod method) {
// We are merging a class B into C. The methods from B are being moved into C, and then we
// subsequently rewrite the invoke-super instructions in C that hit a method in B, such that
// they use an invoke-direct instruction instead. In this process, we need to avoid rewriting
@@ -642,8 +617,7 @@
|| invocationTarget.isNonStaticPrivateMethod();
SynthesizedBridgeCode code =
new SynthesizedBridgeCode(
- newMethod,
- invocationTarget.getReference(),
+ method.getReference(),
invocationTarget.isStatic()
? STATIC
: (invocationTarget.isNonPrivateVirtualMethod() ? VIRTUAL : DIRECT),
@@ -720,8 +694,8 @@
int i = 0;
for (DexEncodedField field : sourceFields) {
DexEncodedField resultingField = renameFieldIfNeeded(field, availableFieldSignatures);
- existingFieldNames.add(resultingField.getReference().name);
- deferredRenamings.map(field.getReference(), resultingField.getReference());
+ existingFieldNames.add(resultingField.getName());
+ deferredRenamings.recordMove(field, resultingField);
result[i] = resultingField;
i++;
}
@@ -759,8 +733,7 @@
DexEncodedMethod result =
method.toTypeSubstitutedMethodAsInlining(newSignature, dexItemFactory);
result.getMutableOptimizationInfo().markForceInline();
- deferredRenamings.map(method.getReference(), result.getReference());
- deferredRenamings.recordMove(method.getReference(), result.getReference());
+ deferredRenamings.recordMove(method, result);
// Renamed constructors turn into ordinary private functions. They can be private, as
// they are only references from their direct subclass, which they were merged into.
result.getAccessFlags().unsetConstructor();
@@ -840,12 +813,13 @@
accessFlags.setPrivate();
}
- private static void makePublic(DexEncodedMethod method) {
+ private static void makePublicFinal(DexEncodedMethod method) {
MethodAccessFlags accessFlags = method.getAccessFlags();
assert !accessFlags.isAbstract();
accessFlags.unsetPrivate();
accessFlags.unsetProtected();
accessFlags.setPublic();
+ accessFlags.setFinal();
}
private void makeStatic(DexEncodedMethod method) {
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/ConnectedComponentVerticalClassMerger.java b/src/main/java/com/android/tools/r8/verticalclassmerging/ConnectedComponentVerticalClassMerger.java
index f984383..22316f3c 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/ConnectedComponentVerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/ConnectedComponentVerticalClassMerger.java
@@ -32,7 +32,7 @@
AppView<AppInfoWithLiveness> appView, Set<DexProgramClass> classesToMerge) {
this.appView = appView;
this.classesToMerge = classesToMerge;
- this.lensBuilder = new VerticalClassMergerGraphLens.Builder(appView);
+ this.lensBuilder = new VerticalClassMergerGraphLens.Builder();
}
public boolean isEmpty() {
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/IllegalAccessDetector.java b/src/main/java/com/android/tools/r8/verticalclassmerging/IllegalAccessDetector.java
index c71a51d..cfbc759 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/IllegalAccessDetector.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/IllegalAccessDetector.java
@@ -20,12 +20,14 @@
public class IllegalAccessDetector extends UseRegistryWithResult<Boolean, ProgramMethod> {
private final AppView<? extends AppInfoWithClassHierarchy> appViewWithClassHierarchy;
+ private final GraphLens codeLens;
public IllegalAccessDetector(
AppView<? extends AppInfoWithClassHierarchy> appViewWithClassHierarchy,
ProgramMethod context) {
super(appViewWithClassHierarchy, context, false);
this.appViewWithClassHierarchy = appViewWithClassHierarchy;
+ this.codeLens = context.getDefinition().getCode().getCodeLens(appViewWithClassHierarchy);
}
protected boolean checkFoundPackagePrivateAccess() {
@@ -43,7 +45,8 @@
}
private boolean checkFieldReference(DexField field) {
- return checkRewrittenFieldReference(appViewWithClassHierarchy.graphLens().lookupField(field));
+ return checkRewrittenFieldReference(
+ appViewWithClassHierarchy.graphLens().lookupField(field, codeLens));
}
private boolean checkRewrittenFieldReference(DexField field) {
@@ -102,16 +105,18 @@
}
private boolean checkTypeReference(DexType type) {
- return internalCheckTypeReference(type, appViewWithClassHierarchy.graphLens());
+ return internalCheckTypeReference(type, appViewWithClassHierarchy.graphLens(), codeLens);
}
private boolean checkRewrittenTypeReference(DexType type) {
- return internalCheckTypeReference(type, GraphLens.getIdentityLens());
+ return internalCheckTypeReference(
+ type, GraphLens.getIdentityLens(), GraphLens.getIdentityLens());
}
- private boolean internalCheckTypeReference(DexType type, GraphLens graphLens) {
+ private boolean internalCheckTypeReference(
+ DexType type, GraphLens graphLens, GraphLens codeLens) {
DexType baseType =
- graphLens.lookupType(type.toBaseType(appViewWithClassHierarchy.dexItemFactory()));
+ graphLens.lookupType(type.toBaseType(appViewWithClassHierarchy.dexItemFactory()), codeLens);
if (baseType.isClassType() && baseType.isSamePackage(getContext().getHolderType())) {
DexClass clazz = appViewWithClassHierarchy.definitionFor(baseType);
if (clazz == null || !clazz.isPublic()) {
@@ -126,7 +131,7 @@
if (appViewWithClassHierarchy.initClassLens().isFinal()) {
// The InitClass lens is always rewritten up until the most recent graph lens, so first map
// the class type to the most recent graph lens.
- DexType rewrittenType = appViewWithClassHierarchy.graphLens().lookupType(clazz);
+ DexType rewrittenType = appViewWithClassHierarchy.graphLens().lookupType(clazz, codeLens);
DexField initClassField =
appViewWithClassHierarchy.initClassLens().getInitClassField(rewrittenType);
checkRewrittenFieldReference(initClassField);
@@ -138,35 +143,35 @@
@Override
public void registerInvokeVirtual(DexMethod method) {
MethodLookupResult lookup =
- appViewWithClassHierarchy.graphLens().lookupInvokeVirtual(method, getContext());
+ appViewWithClassHierarchy.graphLens().lookupInvokeVirtual(method, getContext(), codeLens);
checkRewrittenMethodReference(lookup.getReference(), OptionalBool.FALSE);
}
@Override
public void registerInvokeDirect(DexMethod method) {
MethodLookupResult lookup =
- appViewWithClassHierarchy.graphLens().lookupInvokeDirect(method, getContext());
+ appViewWithClassHierarchy.graphLens().lookupInvokeDirect(method, getContext(), codeLens);
checkRewrittenMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
}
@Override
public void registerInvokeStatic(DexMethod method) {
MethodLookupResult lookup =
- appViewWithClassHierarchy.graphLens().lookupInvokeStatic(method, getContext());
+ appViewWithClassHierarchy.graphLens().lookupInvokeStatic(method, getContext(), codeLens);
checkRewrittenMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
}
@Override
public void registerInvokeInterface(DexMethod method) {
MethodLookupResult lookup =
- appViewWithClassHierarchy.graphLens().lookupInvokeInterface(method, getContext());
+ appViewWithClassHierarchy.graphLens().lookupInvokeInterface(method, getContext(), codeLens);
checkRewrittenMethodReference(lookup.getReference(), OptionalBool.TRUE);
}
@Override
public void registerInvokeSuper(DexMethod method) {
MethodLookupResult lookup =
- appViewWithClassHierarchy.graphLens().lookupInvokeSuper(method, getContext());
+ appViewWithClassHierarchy.graphLens().lookupInvokeSuper(method, getContext(), codeLens);
checkRewrittenMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
}
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/MergeMayLeadToNoSuchMethodErrorUseRegistry.java b/src/main/java/com/android/tools/r8/verticalclassmerging/MergeMayLeadToNoSuchMethodErrorUseRegistry.java
new file mode 100644
index 0000000..c69002e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/MergeMayLeadToNoSuchMethodErrorUseRegistry.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2023, 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.verticalclassmerging;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DefaultUseRegistryWithResult;
+import com.android.tools.r8.graph.DexClassAndMethod;
+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.graph.lens.GraphLens;
+import com.android.tools.r8.graph.lens.MethodLookupResult;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
+class MergeMayLeadToNoSuchMethodErrorUseRegistry
+ extends DefaultUseRegistryWithResult<Boolean, ProgramMethod> {
+
+ private final AppView<AppInfoWithLiveness> appViewWithLiveness;
+ private final GraphLens graphLens;
+ private final GraphLens codeLens;
+ private final DexProgramClass source;
+
+ MergeMayLeadToNoSuchMethodErrorUseRegistry(
+ AppView<AppInfoWithLiveness> appView, ProgramMethod context, DexProgramClass source) {
+ super(appView, context, Boolean.FALSE);
+ assert context.getHolder().getSuperType().isIdenticalTo(source.getType());
+ this.appViewWithLiveness = appView;
+ this.graphLens = appView.graphLens();
+ this.codeLens = context.getDefinition().getCode().getCodeLens(appView);
+ this.source = source;
+ }
+
+ boolean mayLeadToNoSuchMethodError() {
+ return getResult();
+ }
+
+ /**
+ * Sets the result of this registry to true if (1) it finds an invoke-super instruction that
+ * targets a (default) interface method and (2) the invoke-super instruction does not have a
+ * target when the context of the super class is used.
+ */
+ @Override
+ public void registerInvokeSuper(DexMethod method) {
+ MethodLookupResult lookupResult = graphLens.lookupInvokeSuper(method, getContext(), codeLens);
+ DexMethod rewrittenMethod = lookupResult.getReference();
+ MethodResolutionResult currentResolutionResult =
+ appViewWithLiveness.appInfo().unsafeResolveMethodDueToDexFormat(rewrittenMethod);
+ DexClassAndMethod currentSuperTarget =
+ currentResolutionResult.lookupInvokeSuperTarget(getContext(), appViewWithLiveness);
+ if (currentSuperTarget == null || !currentSuperTarget.getHolder().isInterface()) {
+ return;
+ }
+
+ MethodResolutionResult parentResolutionResult =
+ appViewWithLiveness.appInfo().resolveMethodOnClass(source, method);
+ DexClassAndMethod parentSuperTarget =
+ parentResolutionResult.lookupInvokeSuperTarget(source, appViewWithLiveness);
+ if (parentSuperTarget == null) {
+ setResult(true);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/SynthesizedBridgeCode.java b/src/main/java/com/android/tools/r8/verticalclassmerging/SynthesizedBridgeCode.java
index b51cd19..edffcd5 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/SynthesizedBridgeCode.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/SynthesizedBridgeCode.java
@@ -1,9 +1,11 @@
package com.android.tools.r8.verticalclassmerging;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.ir.code.InvokeType;
import com.android.tools.r8.ir.synthetic.AbstractSynthesizedCode;
import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
@@ -13,13 +15,13 @@
private DexMethod method;
private DexMethod invocationTarget;
- private InvokeType type;
+ private final InvokeType type;
private final boolean isInterface;
+ private VerticalClassMergerGraphLens codeLens;
- public SynthesizedBridgeCode(
- DexMethod method, DexMethod invocationTarget, InvokeType type, boolean isInterface) {
+ public SynthesizedBridgeCode(DexMethod method, InvokeType type, boolean isInterface) {
this.method = method;
- this.invocationTarget = invocationTarget;
+ this.invocationTarget = null;
this.type = type;
this.isInterface = isInterface;
}
@@ -44,8 +46,14 @@
// expects to be applied to method signatures from *before* vertical class merging or *after*
// vertical class merging).
public void updateMethodSignatures(VerticalClassMergerGraphLens lens) {
- method = lens.getNextMethodSignature(method);
- invocationTarget = lens.getNextMethodSignature(invocationTarget);
+ codeLens = lens;
+ invocationTarget = lens.getNextImplementationMethodSignature(method);
+ method = lens.getNextBridgeMethodSignature(method);
+ }
+
+ @Override
+ public GraphLens getCodeLens(AppView<?> appView) {
+ return codeLens;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
index fc20047..45f4ca8 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.graph.DexClassAndMethod.asProgramMethodOrNull;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import com.android.tools.r8.classmerging.SyntheticArgumentClass;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
@@ -24,6 +25,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.Timing.TimingMerger;
@@ -32,6 +34,7 @@
import com.google.common.collect.Streams;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -166,15 +169,7 @@
.computeStronglyConnectedComponents();
// Remove singleton class hierarchies as they are not subject to vertical class merging.
- Set<DexProgramClass> singletonComponents = Sets.newIdentityHashSet();
- connectedComponents.removeIf(
- connectedComponent -> {
- if (connectedComponent.size() == 1) {
- singletonComponents.addAll(connectedComponent);
- return true;
- }
- return false;
- });
+ connectedComponents.removeIf(connectedComponent -> connectedComponent.size() == 1);
timing.end();
// Apply class merging concurrently in disjoint class hierarchies.
@@ -185,13 +180,13 @@
if (verticalClassMergerResult.isEmpty()) {
return;
}
-
+ ProfileCollectionAdditions profileCollectionAdditions =
+ ProfileCollectionAdditions.create(appView);
VerticalClassMergerGraphLens lens =
- new VerticalClassMergerTreeFixer(appView, immediateSubtypingInfo, verticalClassMergerResult)
- .run(connectedComponents, singletonComponents, executorService, timing);
+ runFixup(profileCollectionAdditions, verticalClassMergerResult, executorService, timing);
updateKeepInfoForMergedClasses(verticalClassMergerResult);
assert verifyGraphLens(lens, verticalClassMergerResult);
- updateArtProfiles(lens, verticalClassMergerResult);
+ updateArtProfiles(profileCollectionAdditions, lens, verticalClassMergerResult);
appView.rewriteWithLens(lens, executorService, timing);
updateKeepInfoForSynthesizedBridges(verticalClassMergerResult);
appView.notifyOptimizationFinishedForTesting();
@@ -270,12 +265,37 @@
return verticalClassMergerResult.build();
}
+ private VerticalClassMergerGraphLens runFixup(
+ ProfileCollectionAdditions profileCollectionAdditions,
+ VerticalClassMergerResult verticalClassMergerResult,
+ ExecutorService executorService,
+ Timing timing)
+ throws ExecutionException {
+ DexProgramClass deterministicContext =
+ appView
+ .definitionFor(
+ ListUtils.first(
+ ListUtils.sort(
+ verticalClassMergerResult.getVerticallyMergedClasses().getTargets(),
+ Comparator.naturalOrder())))
+ .asProgramClass();
+ SyntheticArgumentClass syntheticArgumentClass =
+ new SyntheticArgumentClass.Builder(appView).build(deterministicContext);
+ VerticalClassMergerGraphLens lens =
+ new VerticalClassMergerTreeFixer(
+ appView,
+ profileCollectionAdditions,
+ syntheticArgumentClass,
+ verticalClassMergerResult)
+ .run(executorService, timing);
+ return lens;
+ }
+
private void updateArtProfiles(
+ ProfileCollectionAdditions profileCollectionAdditions,
VerticalClassMergerGraphLens verticalClassMergerLens,
VerticalClassMergerResult verticalClassMergerResult) {
// Include bridges in art profiles.
- ProfileCollectionAdditions profileCollectionAdditions =
- ProfileCollectionAdditions.create(appView);
if (!profileCollectionAdditions.isNop()) {
List<SynthesizedBridgeCode> synthesizedBridges =
verticalClassMergerResult.getSynthesizedBridges();
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java
index 1d30b3c..3ccd921 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java
@@ -4,14 +4,15 @@
package com.android.tools.r8.verticalclassmerging;
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
import com.android.tools.r8.classmerging.ClassMergerGraphLens;
-import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.graph.lens.MethodLookupResult;
@@ -19,21 +20,23 @@
import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.code.InvokeType;
import com.android.tools.r8.ir.conversion.ExtraParameter;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeHashMap;
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.MutableBidirectionalManyToOneRepresentativeMap;
import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import java.util.Collection;
+import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
// This graph lens is instantiated during vertical class merging. The graph lens is context
@@ -62,35 +65,41 @@
// For the invocation "invoke-virtual A.m()" in B.m2, this graph lens will return the method B.m.
public class VerticalClassMergerGraphLens extends ClassMergerGraphLens {
- public interface GraphLensLookupResultProvider {
-
- MethodLookupResult get(RewrittenPrototypeDescription prototypeChanges);
- }
-
private final VerticallyMergedClasses mergedClasses;
- private final Map<DexType, Map<DexMethod, GraphLensLookupResultProvider>>
- contextualVirtualToDirectMethodMaps;
+ private final Map<DexType, Map<DexMethod, DexMethod>> contextualSuperToImplementationInContexts;
+ private final BidirectionalOneToOneMap<DexMethod, DexMethod> extraNewMethodSignatures;
+
private final Set<DexMethod> mergedMethods;
- private final Map<DexMethod, DexMethod> originalMethodSignaturesForBridges;
- private final Map<DexMethod, RewrittenPrototypeDescription> prototypeChanges;
+ private final Set<DexMethod> staticizedMethods;
private VerticalClassMergerGraphLens(
AppView<?> appView,
VerticallyMergedClasses mergedClasses,
BidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap,
- Map<DexMethod, DexMethod> methodMap,
- Set<DexMethod> mergedMethods,
- Map<DexType, Map<DexMethod, GraphLensLookupResultProvider>>
- contextualVirtualToDirectMethodMaps,
+ Map<DexType, Map<DexMethod, DexMethod>> contextualSuperToImplementationInContexts,
BidirectionalManyToOneRepresentativeMap<DexMethod, DexMethod> newMethodSignatures,
- Map<DexMethod, DexMethod> originalMethodSignaturesForBridges,
- Map<DexMethod, RewrittenPrototypeDescription> prototypeChanges) {
- super(appView, fieldMap, methodMap, mergedClasses.getBidirectionalMap(), newMethodSignatures);
+ BidirectionalOneToOneMap<DexMethod, DexMethod> extraNewMethodSignatures,
+ Set<DexMethod> mergedMethods,
+ Set<DexMethod> staticizedMethods) {
+ super(
+ appView,
+ fieldMap,
+ Collections.emptyMap(),
+ mergedClasses.getBidirectionalMap(),
+ newMethodSignatures);
this.mergedClasses = mergedClasses;
- this.contextualVirtualToDirectMethodMaps = contextualVirtualToDirectMethodMaps;
+ this.contextualSuperToImplementationInContexts = contextualSuperToImplementationInContexts;
+ this.extraNewMethodSignatures = extraNewMethodSignatures;
this.mergedMethods = mergedMethods;
- this.originalMethodSignaturesForBridges = originalMethodSignaturesForBridges;
- this.prototypeChanges = prototypeChanges;
+ this.staticizedMethods = staticizedMethods;
+ }
+
+ private boolean isMerged(DexMethod method) {
+ return mergedMethods.contains(method);
+ }
+
+ private boolean isStaticized(DexMethod method) {
+ return staticizedMethods.contains(method);
}
@Override
@@ -104,6 +113,39 @@
}
@Override
+ public DexField getNextFieldSignature(DexField previous) {
+ DexField field = super.getNextFieldSignature(previous);
+ assert field.verifyReferencedBaseTypesMatches(
+ type -> !mergedClasses.isMergeSource(type), dexItemFactory());
+ return field;
+ }
+
+ @Override
+ public DexMethod getNextMethodSignature(DexMethod previous) {
+ if (extraNewMethodSignatures.containsKey(previous)) {
+ return getNextImplementationMethodSignature(previous);
+ }
+ DexMethod method = super.getNextMethodSignature(previous);
+ assert method.verifyReferencedBaseTypesMatches(
+ type -> !mergedClasses.isMergeSource(type), dexItemFactory());
+ return method;
+ }
+
+ public DexMethod getNextBridgeMethodSignature(DexMethod previous) {
+ DexMethod method = newMethodSignatures.getRepresentativeValueOrDefault(previous, previous);
+ assert method.verifyReferencedBaseTypesMatches(
+ type -> !mergedClasses.isMergeSource(type), dexItemFactory());
+ return method;
+ }
+
+ public DexMethod getNextImplementationMethodSignature(DexMethod previous) {
+ DexMethod method = extraNewMethodSignatures.getRepresentativeValueOrDefault(previous, previous);
+ assert method.verifyReferencedBaseTypesMatches(
+ type -> !mergedClasses.isMergeSource(type), dexItemFactory());
+ return method;
+ }
+
+ @Override
protected Iterable<DexType> internalGetOriginalTypes(DexType previous) {
Collection<DexType> originalTypes = mergedClasses.getSourcesFor(previous);
Iterable<DexType> currentType = IterableUtils.singleton(previous);
@@ -114,57 +156,111 @@
}
@Override
+ protected MethodLookupResult internalLookupMethod(
+ DexMethod reference,
+ DexMethod context,
+ InvokeType type,
+ GraphLens codeLens,
+ LookupMethodContinuation continuation) {
+ if (this == codeLens) {
+ MethodLookupResult lookupResult =
+ MethodLookupResult.builder(this, codeLens)
+ .setReboundReference(reference)
+ .setReference(reference)
+ .setType(type)
+ .build();
+ return continuation.lookupMethod(lookupResult);
+ }
+ return super.internalLookupMethod(reference, context, type, codeLens, continuation);
+ }
+
+ @Override
public MethodLookupResult internalDescribeLookupMethod(
MethodLookupResult previous, DexMethod context, GraphLens codeLens) {
assert context != null || verifyIsContextFreeForMethod(previous.getReference(), codeLens);
assert context == null || previous.getType() != null;
- if (previous.getType() == InvokeType.SUPER && !mergedMethods.contains(context)) {
- Map<DexMethod, GraphLensLookupResultProvider> virtualToDirectMethodMap =
- contextualVirtualToDirectMethodMaps.get(context.getHolderType());
- if (virtualToDirectMethodMap != null) {
- GraphLensLookupResultProvider result =
- virtualToDirectMethodMap.get(previous.getReference());
- if (result != null) {
- // If the super class A of the enclosing class B (i.e., context.holder())
- // has been merged into B during vertical class merging, and this invoke-super instruction
- // was resolving to a method in A, then the target method has been changed to a direct
- // method and moved into B, so that we need to use an invoke-direct instruction instead of
- // invoke-super (or invoke-static, if the method was originally a default interface
- // method).
- return result.get(previous.getPrototypeChanges());
- }
- }
- }
+ assert previous.hasReboundReference();
MethodLookupResult lookupResult;
- DexMethod newMethod = methodMap.apply(previous.getReference());
- if (newMethod == null) {
- lookupResult = previous;
- } else {
+ DexMethod implementationReference = getImplementationTargetForInvokeSuper(previous, context);
+ if (implementationReference != null) {
lookupResult =
- MethodLookupResult.builder(this)
- .setReference(newMethod)
+ MethodLookupResult.builder(this, codeLens)
+ .setReboundReference(implementationReference)
+ .setReference(implementationReference)
.setPrototypeChanges(
- internalDescribePrototypeChanges(previous.getPrototypeChanges(), newMethod))
- .setType(mapInvocationType(newMethod, previous.getReference(), previous.getType()))
+ internalDescribePrototypeChanges(
+ previous.getPrototypeChanges(),
+ previous.getReboundReference(),
+ implementationReference))
+ .setType(
+ isStaticized(implementationReference) ? InvokeType.STATIC : InvokeType.VIRTUAL)
+ .build();
+ } else {
+ DexMethod newReboundReference = previous.getRewrittenReboundReference(newMethodSignatures);
+ assert newReboundReference.verifyReferencedBaseTypesMatches(
+ type -> !mergedClasses.isMergeSource(type), dexItemFactory());
+ DexMethod newReference =
+ previous.getRewrittenReferenceFromRewrittenReboundReference(
+ newReboundReference, this::getNextClassType, dexItemFactory());
+ lookupResult =
+ MethodLookupResult.builder(this, codeLens)
+ .setReboundReference(newReboundReference)
+ .setReference(newReference)
+ .setType(mapInvocationType(newReference, previous.getReference(), previous.getType()))
+ .setPrototypeChanges(
+ internalDescribePrototypeChanges(
+ previous.getPrototypeChanges(),
+ previous.getReboundReference(),
+ newReboundReference))
.build();
}
assert !appView.testing().enableVerticalClassMergerLensAssertion
|| Streams.stream(lookupResult.getReference().getReferencedBaseTypes(dexItemFactory()))
- .noneMatch(type -> mergedClasses.hasBeenMergedIntoSubtype(type));
+ .noneMatch(mergedClasses::hasBeenMergedIntoSubtype);
return lookupResult;
}
+ private DexMethod getImplementationTargetForInvokeSuper(
+ MethodLookupResult previous, DexMethod context) {
+ if (previous.getType().isSuper() && !isMerged(context)) {
+ return contextualSuperToImplementationInContexts
+ .getOrDefault(context.getHolderType(), Collections.emptyMap())
+ .get(previous.getReference());
+ }
+ return null;
+ }
+
@Override
protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
- RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
- return prototypeChanges.combine(
- this.prototypeChanges.getOrDefault(method, RewrittenPrototypeDescription.none()));
+ RewrittenPrototypeDescription prototypeChanges,
+ DexMethod previousMethod,
+ DexMethod newMethod) {
+ if (isStaticized(newMethod)) {
+ // The receiver has been added as an explicit argument.
+ assert newMethod.getArity() == previousMethod.getArity() + 1;
+ RewrittenPrototypeDescription isConvertedToStaticMethod =
+ RewrittenPrototypeDescription.createForArgumentsInfo(
+ ArgumentInfoCollection.builder()
+ .setArgumentInfosSize(newMethod.getParameters().size())
+ .setIsConvertedToStaticMethod()
+ .build());
+ return prototypeChanges.combine(isConvertedToStaticMethod);
+ }
+ if (newMethod.getArity() > previousMethod.getArity()) {
+ assert dexItemFactory().isConstructor(previousMethod);
+ RewrittenPrototypeDescription collisionResolution =
+ RewrittenPrototypeDescription.createForExtraParameters(
+ ExtraUnusedNullParameter.computeExtraUnusedNullParameters(previousMethod, newMethod));
+ return prototypeChanges.combine(collisionResolution);
+ }
+ assert newMethod.getArity() == previousMethod.getArity();
+ return prototypeChanges;
}
@Override
public DexMethod getPreviousMethodSignature(DexMethod method) {
return super.getPreviousMethodSignature(
- originalMethodSignaturesForBridges.getOrDefault(method, method));
+ extraNewMethodSignatures.getRepresentativeKeyOrDefault(method, method));
}
@Override
@@ -175,8 +271,16 @@
@Override
protected InvokeType mapInvocationType(
- DexMethod newMethod, DexMethod originalMethod, InvokeType type) {
- return mapVirtualInterfaceInvocationTypes(appView, newMethod, originalMethod, type);
+ DexMethod newMethod, DexMethod previousMethod, InvokeType type) {
+ if (isStaticized(newMethod)) {
+ return InvokeType.STATIC;
+ }
+ if (type.isInterface()
+ && mergedClasses.hasInterfaceBeenMergedIntoClass(
+ previousMethod.getHolderType(), newMethod.getHolderType())) {
+ return InvokeType.VIRTUAL;
+ }
+ return type;
}
@Override
@@ -184,7 +288,7 @@
if (codeLens == this) {
return true;
}
- return contextualVirtualToDirectMethodMaps.isEmpty()
+ return contextualSuperToImplementationInContexts.isEmpty()
&& getPrevious().isContextFreeForMethods(codeLens);
}
@@ -195,7 +299,7 @@
}
assert getPrevious().verifyIsContextFreeForMethod(method, codeLens);
DexMethod previous = getPrevious().lookupMethod(method, null, null, codeLens).getReference();
- assert contextualVirtualToDirectMethodMaps.values().stream()
+ assert contextualSuperToImplementationInContexts.values().stream()
.noneMatch(virtualToDirectMethodMap -> virtualToDirectMethodMap.containsKey(previous));
return true;
}
@@ -203,115 +307,91 @@
public static class Builder
extends BuilderBase<VerticalClassMergerGraphLens, VerticallyMergedClasses> {
- private final AppView<AppInfoWithLiveness> appView;
- private final DexItemFactory dexItemFactory;
-
- protected final MutableBidirectionalOneToOneMap<DexField, DexField> fieldMap =
+ protected final MutableBidirectionalOneToOneMap<DexField, DexField> newFieldSignatures =
new BidirectionalOneToOneHashMap<>();
- protected final Map<DexMethod, DexMethod> methodMap = new IdentityHashMap<>();
- private final ImmutableSet.Builder<DexMethod> mergedMethodsBuilder = ImmutableSet.builder();
- private final Map<DexType, Map<DexMethod, GraphLensLookupResultProvider>>
- contextualVirtualToDirectMethodMaps = new IdentityHashMap<>();
+ private final Map<DexType, Map<DexMethod, DexMethod>>
+ contextualSuperToImplementationInContexts = new IdentityHashMap<>();
private final MutableBidirectionalManyToOneRepresentativeMap<DexMethod, DexMethod>
newMethodSignatures = BidirectionalManyToOneRepresentativeHashMap.newIdentityHashMap();
- private final Map<DexMethod, DexMethod> originalMethodSignaturesForBridges =
- new IdentityHashMap<>();
- private final Map<DexMethod, RewrittenPrototypeDescription> prototypeChanges =
- new IdentityHashMap<>();
+ private final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> extraNewMethodSignatures =
+ new BidirectionalOneToOneHashMap<>();
- private final Map<DexProto, DexProto> cache = new IdentityHashMap<>();
-
- Builder(AppView<AppInfoWithLiveness> appView) {
- this.appView = appView;
- this.dexItemFactory = appView.dexItemFactory();
- }
+ private final Set<DexMethod> mergedMethods = Sets.newIdentityHashSet();
+ private final Set<DexMethod> staticizedMethods = Sets.newIdentityHashSet();
static Builder createBuilderForFixup(VerticalClassMergerResult verticalClassMergerResult) {
- Builder builder = verticalClassMergerResult.getLensBuilder();
- VerticallyMergedClasses mergedClasses =
- verticalClassMergerResult.getVerticallyMergedClasses();
- Builder newBuilder = new Builder(builder.appView);
- builder.fieldMap.forEach(
- (key, value) ->
- newBuilder.map(
- key, builder.getFieldSignatureAfterClassMerging(value, mergedClasses)));
- for (Map.Entry<DexMethod, DexMethod> entry : builder.methodMap.entrySet()) {
- newBuilder.map(
- entry.getKey(),
- builder.getMethodSignatureAfterClassMerging(entry.getValue(), mergedClasses));
- }
- for (DexMethod method : builder.mergedMethodsBuilder.build()) {
- newBuilder.markMethodAsMerged(
- builder.getMethodSignatureAfterClassMerging(method, mergedClasses));
- }
- for (Map.Entry<DexType, Map<DexMethod, GraphLensLookupResultProvider>> entry :
- builder.contextualVirtualToDirectMethodMaps.entrySet()) {
- DexType context = entry.getKey();
- assert context.isIdenticalTo(builder.getTypeAfterClassMerging(context, mergedClasses));
- for (Map.Entry<DexMethod, GraphLensLookupResultProvider> innerEntry :
- entry.getValue().entrySet()) {
- DexMethod from = innerEntry.getKey();
- MethodLookupResult rewriting =
- innerEntry.getValue().get(RewrittenPrototypeDescription.none());
- DexMethod to =
- builder.getMethodSignatureAfterClassMerging(rewriting.getReference(), mergedClasses);
- newBuilder.mapVirtualMethodToDirectInType(
- from,
- prototypeChanges ->
- new MethodLookupResult(to, null, rewriting.getType(), prototypeChanges),
- context);
- }
- }
- builder.newMethodSignatures.forEachManyToOneMapping(
- (originalMethodSignatures, renamedMethodSignature, representative) -> {
- DexMethod methodSignatureAfterClassMerging =
- builder.getMethodSignatureAfterClassMerging(renamedMethodSignature, mergedClasses);
- newBuilder.newMethodSignatures.put(
- originalMethodSignatures, methodSignatureAfterClassMerging);
- if (originalMethodSignatures.size() > 1) {
- newBuilder.newMethodSignatures.setRepresentative(
- methodSignatureAfterClassMerging, representative);
- }
- });
- for (Map.Entry<DexMethod, DexMethod> entry :
- builder.originalMethodSignaturesForBridges.entrySet()) {
- newBuilder.recordCreationOfBridgeMethod(
- entry.getValue(),
- builder.getMethodSignatureAfterClassMerging(entry.getKey(), mergedClasses));
- }
- builder.prototypeChanges.forEach(
- (method, prototypeChangesForMethod) ->
- newBuilder.prototypeChanges.put(
- builder.getMethodSignatureAfterClassMerging(method, mergedClasses),
- prototypeChangesForMethod));
- return newBuilder;
+ return verticalClassMergerResult.getLensBuilder();
}
@Override
public void addExtraParameters(
- DexMethod methodSignature, List<? extends ExtraParameter> extraParameters) {
- throw new Unimplemented();
+ DexMethod from, DexMethod to, List<? extends ExtraParameter> extraParameters) {
+ // Intentionally empty.
}
@Override
public void commitPendingUpdates() {
- throw new Unimplemented();
+ // Intentionally empty.
}
@Override
- public void fixupField(DexField from, DexField to) {
- throw new Unimplemented();
+ public void fixupField(DexField oldFieldSignature, DexField newFieldSignature) {
+ DexField originalFieldSignature =
+ newFieldSignatures.getKeyOrDefault(oldFieldSignature, oldFieldSignature);
+ newFieldSignatures.put(originalFieldSignature, newFieldSignature);
}
@Override
- public void fixupMethod(DexMethod from, DexMethod to) {
- throw new Unimplemented();
+ public void fixupMethod(DexMethod oldMethodSignature, DexMethod newMethodSignature) {
+ if (extraNewMethodSignatures.containsValue(oldMethodSignature)) {
+ DexMethod originalMethodSignature = extraNewMethodSignatures.getKey(oldMethodSignature);
+ extraNewMethodSignatures.put(originalMethodSignature, newMethodSignature);
+ } else {
+ Set<DexMethod> oldMethodSignatures = newMethodSignatures.getKeys(oldMethodSignature);
+ if (oldMethodSignatures.isEmpty()) {
+ newMethodSignatures.put(oldMethodSignature, newMethodSignature);
+ } else {
+ DexMethod representative = newMethodSignatures.getRepresentativeKey(oldMethodSignature);
+ newMethodSignatures.removeValue(oldMethodSignature);
+ newMethodSignatures.put(oldMethodSignatures, newMethodSignature);
+ if (representative != null) {
+ newMethodSignatures.setRepresentative(newMethodSignature, representative);
+ }
+ }
+ }
+
+ if (mergedMethods.remove(oldMethodSignature)) {
+ mergedMethods.add(newMethodSignature);
+ }
+
+ if (staticizedMethods.remove(oldMethodSignature)) {
+ staticizedMethods.add(newMethodSignature);
+ }
+ }
+
+ public void fixupContextualVirtualToDirectMethodMaps() {
+ for (Entry<DexType, Map<DexMethod, DexMethod>> entry :
+ contextualSuperToImplementationInContexts.entrySet()) {
+ for (Entry<DexMethod, DexMethod> innerEntry : entry.getValue().entrySet()) {
+ DexMethod virtualMethod = innerEntry.getValue();
+ DexMethod implementationMethod = extraNewMethodSignatures.get(virtualMethod);
+ assert implementationMethod != null;
+ innerEntry.setValue(implementationMethod);
+ }
+ }
}
@Override
public Set<DexMethod> getOriginalMethodReferences(DexMethod method) {
- throw new Unimplemented();
+ if (extraNewMethodSignatures.containsValue(method)) {
+ return Set.of(extraNewMethodSignatures.getKey(method));
+ }
+ Set<DexMethod> previousMethodSignatures = newMethodSignatures.getKeys(method);
+ if (!previousMethodSignatures.isEmpty()) {
+ return previousMethodSignatures;
+ }
+ return Set.of(method);
}
@Override
@@ -322,132 +402,70 @@
return new VerticalClassMergerGraphLens(
appView,
mergedClasses,
- fieldMap,
- methodMap,
- mergedMethodsBuilder.build(),
- contextualVirtualToDirectMethodMaps,
+ newFieldSignatures,
+ contextualSuperToImplementationInContexts,
newMethodSignatures,
- originalMethodSignaturesForBridges,
- prototypeChanges);
+ extraNewMethodSignatures,
+ mergedMethods,
+ staticizedMethods);
}
- private DexField getFieldSignatureAfterClassMerging(
- DexField field, VerticallyMergedClasses mergedClasses) {
- assert !field.getHolderType().isArrayType();
-
- DexType holder = field.getHolderType();
- DexType newHolder = mergedClasses.getMergeTargetOrDefault(holder, holder);
-
- DexType type = field.getType();
- DexType newType = getTypeAfterClassMerging(type, mergedClasses);
-
- if (holder.isIdenticalTo(newHolder) && type.isIdenticalTo(newType)) {
- return field;
- }
- return dexItemFactory.createField(newHolder, newType, field.getName());
- }
-
- private DexMethod getMethodSignatureAfterClassMerging(
- DexMethod signature, VerticallyMergedClasses mergedClasses) {
- assert !signature.getHolderType().isArrayType();
-
- DexType holder = signature.getHolderType();
- DexType newHolder = mergedClasses.getMergeTargetOrDefault(holder, holder);
-
- DexProto proto = signature.getProto();
- DexProto newProto =
- dexItemFactory.applyClassMappingToProto(
- proto, type -> getTypeAfterClassMerging(type, mergedClasses), cache);
-
- if (holder.isIdenticalTo(newHolder) && proto.isIdenticalTo(newProto)) {
- return signature;
- }
- return dexItemFactory.createMethod(newHolder, newProto, signature.getName());
- }
-
- private DexType getTypeAfterClassMerging(DexType type, VerticallyMergedClasses mergedClasses) {
- if (type.isArrayType()) {
- DexType baseType = type.toBaseType(dexItemFactory);
- DexType newBaseType = mergedClasses.getMergeTargetOrDefault(baseType, baseType);
- if (newBaseType.isNotIdenticalTo(baseType)) {
- return type.replaceBaseType(newBaseType, dexItemFactory);
- }
- return type;
- }
- return mergedClasses.getMergeTargetOrDefault(type, type);
- }
-
+ // TODO: should be removed.
public boolean hasMappingForSignatureInContext(DexProgramClass context, DexMethod signature) {
- Map<DexMethod, GraphLensLookupResultProvider> virtualToDirectMethodMap =
- contextualVirtualToDirectMethodMaps.get(context.type);
- if (virtualToDirectMethodMap != null) {
- return virtualToDirectMethodMap.containsKey(signature);
+ return contextualSuperToImplementationInContexts
+ .getOrDefault(context.getType(), Collections.emptyMap())
+ .containsKey(signature);
+ }
+
+ public void markMethodAsMerged(DexEncodedMethod method) {
+ mergedMethods.add(method.getReference());
+ }
+
+ public void recordMove(DexEncodedField from, DexEncodedField to) {
+ newFieldSignatures.put(from.getReference(), to.getReference());
+ }
+
+ public void recordMove(DexEncodedMethod from, DexEncodedMethod to) {
+ newMethodSignatures.put(from.getReference(), to.getReference());
+ }
+
+ public void recordSplit(
+ DexEncodedMethod from,
+ DexEncodedMethod override,
+ DexEncodedMethod bridge,
+ DexEncodedMethod implementation) {
+ if (override != null) {
+ assert bridge == null;
+ newMethodSignatures.put(from.getReference(), override.getReference());
+ newMethodSignatures.put(override.getReference(), override.getReference());
+ newMethodSignatures.setRepresentative(override.getReference(), override.getReference());
+ } else {
+ assert bridge != null;
+ newMethodSignatures.put(from.getReference(), bridge.getReference());
}
- return false;
- }
- public boolean hasOriginalSignatureMappingFor(DexField field) {
- return fieldMap.containsValue(field);
- }
+ if (implementation == null) {
+ assert from.isAbstract();
+ return;
+ }
- public boolean hasOriginalSignatureMappingFor(DexMethod method) {
- return newMethodSignatures.containsValue(method)
- || originalMethodSignaturesForBridges.containsKey(method);
- }
+ extraNewMethodSignatures.put(from.getReference(), implementation.getReference());
+ mergedMethods.add(implementation.getReference());
- public void markMethodAsMerged(DexMethod method) {
- mergedMethodsBuilder.add(method);
- }
-
- public void map(DexField from, DexField to) {
- fieldMap.put(from, to);
- }
-
- public Builder map(DexMethod from, DexMethod to) {
- methodMap.put(from, to);
- return this;
- }
-
- public void recordMerge(DexMethod from, DexMethod to) {
- newMethodSignatures.put(from, to);
- newMethodSignatures.put(to, to);
- newMethodSignatures.setRepresentative(to, to);
- }
-
- public void recordMove(DexMethod from, DexMethod to) {
- recordMove(from, to, false);
- }
-
- public void recordMove(DexMethod from, DexMethod to, boolean isStaticized) {
- newMethodSignatures.put(from, to);
- if (isStaticized) {
- RewrittenPrototypeDescription prototypeChangesForMethod =
- RewrittenPrototypeDescription.create(
- ImmutableList.of(),
- null,
- ArgumentInfoCollection.builder()
- .setArgumentInfosSize(to.getParameters().size())
- .setIsConvertedToStaticMethod()
- .build());
- prototypeChanges.put(to, prototypeChangesForMethod);
+ if (implementation.isStatic()) {
+ staticizedMethods.add(implementation.getReference());
}
}
- public void recordCreationOfBridgeMethod(DexMethod from, DexMethod to) {
- originalMethodSignaturesForBridges.put(to, from);
- }
-
- public void mapVirtualMethodToDirectInType(
- DexMethod from, GraphLensLookupResultProvider to, DexType type) {
- Map<DexMethod, GraphLensLookupResultProvider> virtualToDirectMethodMap =
- contextualVirtualToDirectMethodMaps.computeIfAbsent(type, key -> new IdentityHashMap<>());
- virtualToDirectMethodMap.put(from, to);
+ public void mapVirtualMethodToDirectInType(DexMethod from, DexEncodedMethod to, DexType type) {
+ contextualSuperToImplementationInContexts
+ .computeIfAbsent(type, ignoreKey(IdentityHashMap::new))
+ .put(from, to.getReference());
}
public void merge(VerticalClassMergerGraphLens.Builder builder) {
- fieldMap.putAll(builder.fieldMap);
- methodMap.putAll(builder.methodMap);
- mergedMethodsBuilder.addAll(builder.mergedMethodsBuilder.build());
+ newFieldSignatures.putAll(builder.newFieldSignatures);
+ mergedMethods.addAll(builder.mergedMethods);
builder.newMethodSignatures.forEachManyToOneMapping(
(keys, value, representative) -> {
boolean isRemapping =
@@ -482,19 +500,13 @@
}
}
});
- prototypeChanges.putAll(builder.prototypeChanges);
- originalMethodSignaturesForBridges.putAll(builder.originalMethodSignaturesForBridges);
- for (DexType context : builder.contextualVirtualToDirectMethodMaps.keySet()) {
- Map<DexMethod, GraphLensLookupResultProvider> current =
- contextualVirtualToDirectMethodMaps.get(context);
- Map<DexMethod, GraphLensLookupResultProvider> other =
- builder.contextualVirtualToDirectMethodMaps.get(context);
- if (current != null) {
- current.putAll(other);
- } else {
- contextualVirtualToDirectMethodMaps.put(context, other);
- }
- }
+ staticizedMethods.addAll(builder.staticizedMethods);
+ extraNewMethodSignatures.putAll(builder.extraNewMethodSignatures);
+ builder.contextualSuperToImplementationInContexts.forEach(
+ (key, value) ->
+ contextualSuperToImplementationInContexts
+ .computeIfAbsent(key, ignoreKey(IdentityHashMap::new))
+ .putAll(value));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java
index 45796d3..983077e 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java
@@ -70,7 +70,8 @@
if (!isStillMergeCandidate(sourceClass, targetClass)) {
continue;
}
- if (mergeMayLeadToIllegalAccesses(sourceClass, targetClass)) {
+ if (mergeMayLeadToIllegalAccesses(sourceClass, targetClass)
+ || mergeMayLeadToNoSuchMethodError(sourceClass, targetClass)) {
continue;
}
mergeCandidates.add(sourceClass);
@@ -328,10 +329,35 @@
return TraversalContinuation.doBreak();
}
return TraversalContinuation.doContinue();
- });
+ },
+ DexEncodedMethod::hasCode);
return result.shouldBreak();
}
+ // TODO: maybe skip this check if target does not implement any interfaces (directly or
+ // indirectly)?
+ private boolean mergeMayLeadToNoSuchMethodError(DexProgramClass source, DexProgramClass target) {
+ // This only returns true when an invoke-super instruction is found that targets a default
+ // interface method.
+ if (!options.canUseDefaultAndStaticInterfaceMethods()) {
+ return false;
+ }
+ // This problem may only arise when merging (non-interface) classes into classes.
+ if (source.isInterface() || target.isInterface()) {
+ return false;
+ }
+ return target
+ .traverseProgramMethods(
+ method -> {
+ MergeMayLeadToNoSuchMethodErrorUseRegistry registry =
+ new MergeMayLeadToNoSuchMethodErrorUseRegistry(appView, method, source);
+ method.registerCodeReferencesWithResult(registry);
+ return TraversalContinuation.breakIf(registry.mayLeadToNoSuchMethodError());
+ },
+ DexEncodedMethod::hasCode)
+ .shouldBreak();
+ }
+
private boolean methodResolutionMayChange(DexProgramClass source, DexProgramClass target) {
for (DexEncodedMethod virtualSourceMethod : source.virtualMethods()) {
DexEncodedMethod directTargetMethod =
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerResult.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerResult.java
index a98e6c3..390131a 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerResult.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerResult.java
@@ -58,7 +58,7 @@
Builder(AppView<AppInfoWithLiveness> appView) {
this(
- new VerticalClassMergerGraphLens.Builder(appView),
+ new VerticalClassMergerGraphLens.Builder(),
new ArrayList<>(),
VerticallyMergedClasses.builder());
}
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerTreeFixer.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerTreeFixer.java
index d424f66..6b9955c 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerTreeFixer.java
@@ -3,104 +3,55 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.verticalclassmerging;
-import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.classmerging.ClassMergerTreeFixer;
+import com.android.tools.r8.classmerging.SyntheticArgumentClass;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
-import com.android.tools.r8.graph.fixup.TreeFixerBase;
-import com.android.tools.r8.shaking.AnnotationFixer;
+import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.Timing;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-class VerticalClassMergerTreeFixer extends TreeFixerBase {
+class VerticalClassMergerTreeFixer
+ extends ClassMergerTreeFixer<
+ VerticalClassMergerGraphLens.Builder,
+ VerticalClassMergerGraphLens,
+ VerticallyMergedClasses> {
- private final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
- private final VerticalClassMergerGraphLens.Builder lensBuilder;
- private final VerticallyMergedClasses mergedClasses;
private final List<SynthesizedBridgeCode> synthesizedBridges;
VerticalClassMergerTreeFixer(
AppView<AppInfoWithLiveness> appView,
- ImmediateProgramSubtypingInfo immediateSubtypingInfo,
+ ProfileCollectionAdditions profileCollectionAdditions,
+ SyntheticArgumentClass syntheticArgumentClass,
VerticalClassMergerResult verticalClassMergerResult) {
- super(appView);
- this.immediateSubtypingInfo = immediateSubtypingInfo;
- this.lensBuilder =
- VerticalClassMergerGraphLens.Builder.createBuilderForFixup(verticalClassMergerResult);
- this.mergedClasses = verticalClassMergerResult.getVerticallyMergedClasses();
+ super(
+ appView,
+ VerticalClassMergerGraphLens.Builder.createBuilderForFixup(verticalClassMergerResult),
+ verticalClassMergerResult.getVerticallyMergedClasses(),
+ profileCollectionAdditions,
+ syntheticArgumentClass);
this.synthesizedBridges = verticalClassMergerResult.getSynthesizedBridges();
}
- VerticalClassMergerGraphLens run(
- List<Set<DexProgramClass>> connectedComponents,
- Set<DexProgramClass> singletonComponents,
- ExecutorService executorService,
- Timing timing)
+ @Override
+ public VerticalClassMergerGraphLens run(ExecutorService executorService, Timing timing)
throws ExecutionException {
- timing.begin("Fixup");
- // Globally substitute merged class types in protos and holders.
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- clazz.getMethodCollection().replaceMethods(this::fixupMethod);
- clazz.setStaticFields(fixupFields(clazz.staticFields()));
- clazz.setInstanceFields(fixupFields(clazz.instanceFields()));
- clazz.setPermittedSubclassAttributes(
- fixupPermittedSubclassAttribute(clazz.getPermittedSubclassAttributes()));
- }
- VerticalClassMergerGraphLens lens = lensBuilder.build(appView, mergedClasses);
+ VerticalClassMergerGraphLens lens = super.run(executorService, timing);
for (SynthesizedBridgeCode synthesizedBridge : synthesizedBridges) {
synthesizedBridge.updateMethodSignatures(lens);
}
- new AnnotationFixer(appView, lens).run(appView.appInfo().classes(), executorService);
- timing.end();
return lens;
}
@Override
- public DexType mapClassType(DexType type) {
- while (mergedClasses.hasBeenMergedIntoSubtype(type)) {
- type = mergedClasses.getTargetFor(type);
- }
- return type;
+ public void postprocess() {
+ lensBuilder.fixupContextualVirtualToDirectMethodMaps();
}
@Override
- public void recordClassChange(DexType from, DexType to) {
- // Fixup of classes is not used so no class type should change.
- throw new Unreachable();
- }
-
- @Override
- public void recordFieldChange(DexField from, DexField to) {
- if (!lensBuilder.hasOriginalSignatureMappingFor(to)) {
- lensBuilder.map(from, to);
- }
- }
-
- @Override
- public void recordMethodChange(DexMethod from, DexMethod to) {
- if (!lensBuilder.hasOriginalSignatureMappingFor(to)) {
- lensBuilder.map(from, to).recordMove(from, to);
- }
- }
-
- @Override
- public DexEncodedMethod recordMethodChange(DexEncodedMethod method, DexEncodedMethod newMethod) {
- recordMethodChange(method.getReference(), newMethod.getReference());
- if (newMethod.isNonPrivateVirtualMethod()) {
- // Since we changed the return type or one of the parameters, this method cannot be a
- // classpath or library method override, since we only class merge program classes.
- assert !method.isLibraryMethodOverride().isTrue();
- newMethod.setLibraryMethodOverride(OptionalBool.FALSE);
- }
- return newMethod;
+ public boolean isRunningBeforePrimaryOptimizationPass() {
+ return true;
}
}
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticallyMergedClasses.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticallyMergedClasses.java
index 394b067..0003478 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticallyMergedClasses.java
@@ -22,13 +22,16 @@
public class VerticallyMergedClasses implements MergedClasses {
private final BidirectionalManyToOneRepresentativeMap<DexType, DexType> mergedClasses;
- private final BidirectionalManyToOneMap<DexType, DexType> mergedInterfaces;
+ private final BidirectionalManyToOneMap<DexType, DexType> mergedInterfacesToClasses;
+ private final BidirectionalManyToOneMap<DexType, DexType> mergedInterfacesToInterfaces;
public VerticallyMergedClasses(
BidirectionalManyToOneRepresentativeMap<DexType, DexType> mergedClasses,
- BidirectionalManyToOneMap<DexType, DexType> mergedInterfaces) {
+ BidirectionalManyToOneMap<DexType, DexType> mergedInterfacesToClasses,
+ BidirectionalManyToOneMap<DexType, DexType> mergedInterfacesToInterfaces) {
this.mergedClasses = mergedClasses;
- this.mergedInterfaces = mergedInterfaces;
+ this.mergedInterfacesToClasses = mergedInterfacesToClasses;
+ this.mergedInterfacesToInterfaces = mergedInterfacesToInterfaces;
}
public static Builder builder() {
@@ -38,7 +41,7 @@
public static VerticallyMergedClasses empty() {
EmptyBidirectionalOneToOneMap<DexType, DexType> emptyMap =
new EmptyBidirectionalOneToOneMap<>();
- return new VerticallyMergedClasses(emptyMap, emptyMap);
+ return new VerticallyMergedClasses(emptyMap, emptyMap, emptyMap);
}
@Override
@@ -59,6 +62,10 @@
return mergedClasses.keySet();
}
+ public Set<DexType> getTargets() {
+ return mergedClasses.values();
+ }
+
public Collection<DexType> getSourcesFor(DexType type) {
return mergedClasses.getKeys(type);
}
@@ -77,8 +84,13 @@
return mergedClasses.containsKey(type);
}
+ public boolean hasInterfaceBeenMergedIntoClass(DexType interfaceType, DexType classType) {
+ return classType.isIdenticalTo(mergedInterfacesToClasses.get(interfaceType));
+ }
+
public boolean hasInterfaceBeenMergedIntoSubtype(DexType type) {
- return mergedInterfaces.containsKey(type);
+ return mergedInterfacesToClasses.containsKey(type)
+ || mergedInterfacesToInterfaces.containsKey(type);
}
public boolean isEmpty() {
@@ -104,13 +116,20 @@
private final MutableBidirectionalManyToOneRepresentativeMap<DexType, DexType> mergedClasses =
BidirectionalManyToOneRepresentativeHashMap.newIdentityHashMap();
- private final BidirectionalManyToOneHashMap<DexType, DexType> mergedInterfaces =
+ private final BidirectionalManyToOneHashMap<DexType, DexType> mergedInterfacesToClasses =
+ BidirectionalManyToOneHashMap.newIdentityHashMap();
+
+ private final BidirectionalManyToOneHashMap<DexType, DexType> mergedInterfacesToInterfaces =
BidirectionalManyToOneHashMap.newIdentityHashMap();
void add(DexProgramClass source, DexProgramClass target) {
mergedClasses.put(source.getType(), target.getType());
if (source.isInterface()) {
- mergedInterfaces.put(source.getType(), target.getType());
+ if (target.isInterface()) {
+ mergedInterfacesToInterfaces.put(source.getType(), target.getType());
+ } else {
+ mergedInterfacesToClasses.put(source.getType(), target.getType());
+ }
}
}
@@ -128,11 +147,13 @@
void merge(VerticallyMergedClasses.Builder other) {
mergedClasses.putAll(other.mergedClasses);
- mergedInterfaces.putAll(other.mergedInterfaces);
+ mergedInterfacesToClasses.putAll(other.mergedInterfacesToClasses);
+ mergedInterfacesToInterfaces.putAll(other.mergedInterfacesToInterfaces);
}
VerticallyMergedClasses build() {
- return new VerticallyMergedClasses(mergedClasses, mergedInterfaces);
+ return new VerticallyMergedClasses(
+ mergedClasses, mergedInterfacesToClasses, mergedInterfacesToInterfaces);
}
}
}
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 0bc2093..0dbe829 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -1176,7 +1176,7 @@
// These tests match on paths relative to the execution directory (normally the repo root).
// Cached stdout might be from a different directory.
private static List<String> noArtCommandCaching =
- ImmutableList.of("068-classloader", "086-null-superTest", "087-gc-after-linkTest");
+ ImmutableList.of("068-classloader", "086-null-super", "087-gc-after-link");
private static final String NO_CLASS_ACCESS_MODIFICATION_RULE =
"-keep,allowobfuscation,allowoptimization,allowshrinking class *";
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerSubClassCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerSubClassCollisionTest.java
index e180f05..7b685b6 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerSubClassCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerSubClassCollisionTest.java
@@ -71,6 +71,7 @@
@NoHorizontalClassMerging
@NeverClassInline
+ @NoVerticalClassMerging
public static class C {
@NeverInline
public void foo(A a) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/ConflictWasDetectedTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/ConflictWasDetectedTest.java
index b1185f0..3eec75f 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/ConflictWasDetectedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/ConflictWasDetectedTest.java
@@ -6,37 +6,42 @@
import com.android.tools.r8.KeepUnusedArguments;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class ConflictWasDetectedTest extends TestBase {
- private final TestParameters parameters;
+ @Parameter(0)
+ public TestParameters parameters;
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- public ConflictWasDetectedTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
@Test
public void test() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector
+ .assertClassesMerged(
+ ClassWithConflictingMethod.class, OtherClassWithConflictingMethod.class)
+ .assertNoOtherClassesMerged())
.addVerticallyMergedClassesInspector(
- VerticallyMergedClassesInspector::assertNoClassesMerged)
+ inspector -> inspector.assertMergedIntoSubtype(ConflictingInterface.class))
.enableInliningAnnotations()
- // .enableNoHorizontalClassMergingAnnotations()
+ .enableNoParameterTypeStrengtheningAnnotations()
.enableUnusedArgumentAnnotations()
.setMinApi(parameters)
.compile()
@@ -53,6 +58,8 @@
escape(impl);
}
+ @NeverInline
+ @NoParameterTypeStrengthening
private static void callMethodOnIface(ConflictingInterface iface) {
System.out.println(iface.method());
System.out.println(ClassWithConflictingMethod.conflict(null));
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/IncorrectRewritingOfInvokeSuperTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/IncorrectRewritingOfInvokeSuperTest.java
index 8cd641d..513561a 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/IncorrectRewritingOfInvokeSuperTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/IncorrectRewritingOfInvokeSuperTest.java
@@ -4,15 +4,11 @@
package com.android.tools.r8.classmerging.vertical;
-import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.Box;
-import com.android.tools.r8.utils.codeinspector.AssertUtils;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,36 +34,31 @@
@Test
public void test() throws Exception {
- Box<R8TestCompileResult> compileResult = new Box<>();
- AssertUtils.assertFailsCompilationIf(
- verifyLensLookup,
- () ->
- compileResult.set(
- testForR8(parameters.getBackend())
- .addInnerClasses(IncorrectRewritingOfInvokeSuperTest.class)
- .addKeepMainRule(TestClass.class)
- .addOptionsModification(
- options -> {
- options.enableUnusedInterfaceRemoval = false;
- options.testing.enableVerticalClassMergerLensAssertion = verifyLensLookup;
- })
- .enableInliningAnnotations()
- .addDontObfuscate()
- .setMinApi(parameters)
- .compile()));
-
- if (!compileResult.isSet()) {
- assertTrue(verifyLensLookup);
- return;
- }
-
- compileResult.get().run(parameters.getRuntime(), TestClass.class).assertSuccess();
+ testForR8(parameters.getBackend())
+ .addInnerClasses(IncorrectRewritingOfInvokeSuperTest.class)
+ .addKeepMainRule(TestClass.class)
+ .addOptionsModification(
+ options -> {
+ options.enableUnusedInterfaceRemoval = false;
+ options.testing.enableVerticalClassMergerLensAssertion = verifyLensLookup;
+ })
+ .enableInliningAnnotations()
+ .addDontObfuscate()
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Caught NPE");
}
static class TestClass {
public static void main(String[] args) {
- new B() {}.m(new SubArgType());
+ B b = new B() {};
+ b.m(new SubArgType());
+ try {
+ b.m(null);
+ } catch (RuntimeException e) {
+ System.out.println("Caught NPE");
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/MethodCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/MethodCollisionTest.java
index ea8f326..3555fa6 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/MethodCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/MethodCollisionTest.java
@@ -9,37 +9,30 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class MethodCollisionTest extends TestBase {
- private final TestParameters parameters;
+ @Parameter(0)
+ public TestParameters parameters;
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- public MethodCollisionTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
@Test
public void test() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
- // TODO(christofferqa): Currently we do not allow merging A into B because we find a
- // collision. However, we are free to change the names of private methods, so we should
- // handle them similar to fields (i.e., we should allow merging A into B). This would also
- // improve the performance of the collision detector, because it would only have to
- // consider non-private methods.
.addVerticallyMergedClassesInspector(
- VerticallyMergedClassesInspector::assertNoClassesMerged)
+ inspector -> inspector.assertMergedIntoSubtype(A.class, C.class))
.enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters)
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 7db55ae..7254cd5 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
@@ -145,7 +145,6 @@
Set<String> preservedClassNames =
ImmutableSet.of(
"classmerging.ArrayTypeCollisionTest",
- "classmerging.ArrayTypeCollisionTest$A",
"classmerging.ArrayTypeCollisionTest$B");
runTest(
testForR8(parameters.getBackend())
@@ -177,8 +176,7 @@
public void testArrayReturnTypeCollision() throws Throwable {
String main = "classmerging.ArrayReturnTypeCollisionTest";
Set<String> preservedClassNames =
- ImmutableSet.of(
- "classmerging.ArrayReturnTypeCollisionTest", "classmerging.A", "classmerging.B");
+ ImmutableSet.of("classmerging.ArrayReturnTypeCollisionTest", "classmerging.B");
JasminBuilder jasminBuilder = new JasminBuilder();
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithMissingTypeArgsSubstitutionTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithMissingTypeArgsSubstitutionTest.java
index d5bfade..de57198 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithMissingTypeArgsSubstitutionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithMissingTypeArgsSubstitutionTest.java
@@ -34,7 +34,7 @@
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- @Test()
+ @Test
public void test() throws Exception {
testForR8Compat(parameters.getBackend())
.addInnerClasses(getClass())
@@ -63,14 +63,22 @@
// copied to the virtual bridge.
MethodSubject fooMovedFromB =
classSubject.uniqueMethodThatMatches(
- method -> !method.isVirtual() && method.getOriginalName(false).equals("foo"));
+ method ->
+ method.isFinal()
+ && method.isVirtual()
+ && !method.isSynthetic()
+ && method.getOriginalName(false).equals("foo"));
assertThat(fooMovedFromB, isPresentAndRenamed());
assertEquals(
"(Ljava/lang/Object;)Ljava/lang/Object;",
fooMovedFromB.getFinalSignatureAttribute());
MethodSubject fooBridge =
classSubject.uniqueMethodThatMatches(
- method -> method.isVirtual() && method.getOriginalName(false).equals("foo"));
+ method ->
+ method.isFinal()
+ && method.isVirtual()
+ && method.isSynthetic()
+ && method.getOriginalName(false).equals("foo"));
assertThat(fooBridge, isPresentAndRenamed());
assertEquals(
"(Ljava/lang/Object;)Ljava/lang/Object;", fooBridge.getFinalSignatureAttribute());
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
index 0a7c200..e4847bd 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.PACKAGE_NAME;
import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.classesMatching;
import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertSame;
import static junit.framework.TestCase.assertTrue;
@@ -151,7 +152,7 @@
}
public static void assertNestAttributesCorrect(CodeInspector inspector) {
- assertTrue(inspector.allClasses().size() > 0);
+ assertFalse(inspector.allClasses().isEmpty());
for (FoundClassSubject classSubject : inspector.allClasses()) {
DexClass clazz = classSubject.getDexProgramClass();
if (clazz.isInANest()) {
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridge3Test.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridge3Test.java
index f0955fc..e070f46 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridge3Test.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridge3Test.java
@@ -9,6 +9,7 @@
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
+import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -43,13 +44,17 @@
testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
.addProgramClasses(I.class, A.class, Main.class)
.addProgramClassFileData(getClassWithTransformedInvoked())
- .run(parameters.getRuntime(), Main.class);
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::inspectRunResult);
+ }
+
+ private void inspectRunResult(SingleTestRunResult<?> runResult) {
if (parameters.isDexRuntime() && parameters.canUseDefaultAndStaticInterfaceMethods()) {
// TODO(b/166210854): Runs really should fail, but since DEX does not have interface
// method references the VM will just dispatch.
- result.assertSuccessWithOutput(EXPECTED);
+ runResult.assertSuccessWithOutput(EXPECTED);
} else {
- result.assertFailureWithErrorThatThrows(getExpectedException());
+ runResult.assertFailureWithErrorThatThrows(getExpectedException());
}
}
@@ -74,8 +79,11 @@
.addKeepMainRule(Main.class)
.setMinApi(parameters)
.run(parameters.getRuntime(), Main.class)
- // TODO(b/166210854): Runs but should have failed.
- .assertSuccessWithOutput(EXPECTED);
+ .applyIf(
+ parameters.isCfRuntime(),
+ // TODO(b/166210854): Runs but should have failed.
+ runResult -> runResult.assertSuccessWithOutput(EXPECTED),
+ this::inspectRunResult);
}
private byte[] getClassWithTransformedInvoked() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridgeTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridgeTest.java
index e89317f..0421acd 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridgeTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridgeTest.java
@@ -73,7 +73,7 @@
public static class A implements I {}
- public static class B extends A {
+ public static class B extends A { // B extends Object implements I
public void bar() {
foo(); // Will be rewritten to invoke-special B.foo()
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToImmediateInterfaceTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToImmediateInterfaceTest.java
index 15f1e9c..455e037 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToImmediateInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToImmediateInterfaceTest.java
@@ -6,17 +6,13 @@
import static com.android.tools.r8.utils.DescriptorUtils.getBinaryNameFromJavaType;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
-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.ToolHelper.DexVm.Version;
-import com.android.tools.r8.utils.Box;
-import com.android.tools.r8.utils.codeinspector.AssertUtils;
import java.io.IOException;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -63,30 +59,17 @@
@Test
public void testR8() throws Exception {
- Box<R8TestCompileResult> compileResult = new Box<>();
-
- // TODO(b/313065227): Should succeed.
- AssertUtils.assertFailsCompilationIf(
- parameters.isCfRuntime(),
- () ->
- testForR8(parameters.getBackend())
- .addProgramClasses(I.class, Main.class)
- .addProgramClassFileData(getClassWithTransformedInvoked())
- .addKeepMainRule(Main.class)
- .setMinApi(parameters)
- .compile()
- .apply(compileResult::set));
-
- if (!compileResult.isSet()) {
- assertTrue(parameters.isCfRuntime());
- return;
- }
-
- // TODO(b/313065227): Should succeed.
- compileResult
- .get()
+ testForR8(parameters.getBackend())
+ .addProgramClasses(I.class, Main.class)
+ .addProgramClassFileData(getClassWithTransformedInvoked())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters)
.run(parameters.getRuntime(), Main.class)
- .assertFailureWithErrorThatThrows(NullPointerException.class);
+ // TODO(b/313065227): Should succeed.
+ .applyIf(
+ parameters.isCfRuntime(),
+ runResult -> runResult.assertFailureWithErrorThatThrows(NoSuchMethodError.class),
+ runResult -> runResult.assertFailureWithErrorThatThrows(NullPointerException.class));
}
private byte[] getClassWithTransformedInvoked() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
index 9d261da..2fd2c50 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
@@ -10,6 +10,8 @@
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.KeepUnusedReturnValue;
+import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestParameters;
@@ -17,8 +19,6 @@
import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -26,7 +26,6 @@
@RunWith(Parameterized.class)
public class VerticalClassMergingRetraceTest extends RetraceTestBase {
- private Set<StackTraceLine> haveSeenLines = new HashSet<>();
@Parameters(name = "{0}, mode: {1}, compat: {2}")
public static Collection<Object[]> data() {
@@ -43,7 +42,10 @@
@Override
public void configure(R8TestBuilder builder) {
- builder.enableInliningAnnotations();
+ builder
+ .enableInliningAnnotations()
+ .enableKeepUnusedReturnValueAnnotations()
+ .enableNeverClassInliningAnnotations();
}
@Override
@@ -126,7 +128,6 @@
// since the synthetic bridge belongs to ResourceWrapper.foo.
assumeTrue(compat);
assumeTrue(parameters.isDexRuntime());
- haveSeenLines.clear();
runTest(
ImmutableList.of(),
(StackTrace actualStackTrace, StackTrace retracedStackTrace) -> {
@@ -142,11 +143,14 @@
// Will be merged down, and represented as:
// java.lang.String ...ResourceWrapper.foo() -> a
@NeverInline
+ // TODO(b/313404813): Remove @KeepUnusedReturnValue as a workaround for a retrace failure.
+ @KeepUnusedReturnValue
String foo() {
throw null;
}
}
+@NeverClassInline
class TintResources extends ResourceWrapper {}
class MainApp {
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/VerticalClassMergingBridgeProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/VerticalClassMergingBridgeProfileRewritingTest.java
index b04e01b..56ea0b9 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/VerticalClassMergingBridgeProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/VerticalClassMergingBridgeProfileRewritingTest.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
+import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -16,7 +17,6 @@
import com.android.tools.r8.utils.InternalOptions.InlinerOptions;
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.MethodSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,6 +46,7 @@
options -> options.callSiteOptimizationOptions().disableOptimization())
.addVerticallyMergedClassesInspector(
inspector -> inspector.assertMergedIntoSubtype(A.class))
+ .enableNeverClassInliningAnnotations()
.setMinApi(parameters)
.compile()
.inspectResidualArtProfile(this::inspect)
@@ -64,11 +65,13 @@
assertThat(bClassSubject, isPresent());
MethodSubject movedMethodSubject =
- bClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isPrivate);
+ bClassSubject.uniqueMethodThatMatches(
+ method -> method.isBridge() && method.isSynthetic() && method.isVirtual());
assertThat(movedMethodSubject, isPresent());
MethodSubject syntheticBridgeMethodSubject =
- bClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
+ bClassSubject.uniqueMethodThatMatches(
+ method -> !method.isBridge() && !method.isSynthetic() && method.isVirtual());
assertThat(syntheticBridgeMethodSubject, isPresent());
profileInspector
@@ -90,5 +93,6 @@
}
}
+ @NeverClassInline
static class B extends A {}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
index 9d3f2c5..0fe9536 100644
--- a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
@@ -4,8 +4,7 @@
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 com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -142,28 +141,22 @@
.inspector();
ClassSubject superInterface1 = inspector.clazz(B112452064SuperInterface1.class);
- if (enableUnusedInterfaceRemoval && enableVerticalClassMerging) {
- assertThat(superInterface1, isAbsent());
- } else {
- assertThat(superInterface1, isPresentAndRenamed());
- }
+ assertThat(
+ superInterface1, isAbsentIf(enableUnusedInterfaceRemoval && enableVerticalClassMerging));
+
MethodSubject foo = superInterface1.uniqueMethodWithOriginalName("foo");
- assertThat(foo, not(isPresent()));
+ assertThat(foo, isAbsent());
+
ClassSubject superInterface2 = inspector.clazz(B112452064SuperInterface2.class);
- if (enableVerticalClassMerging) {
- assertThat(superInterface2, not(isPresent()));
- } else {
- assertThat(superInterface2, isPresentAndRenamed());
- }
+ assertThat(
+ superInterface2, isAbsentIf(enableUnusedInterfaceRemoval && enableVerticalClassMerging));
+
MethodSubject bar = superInterface2.uniqueMethodWithOriginalName("bar");
- assertThat(bar, not(isPresent()));
+ assertThat(bar, isAbsent());
+
ClassSubject subInterface = inspector.clazz(B112452064SubInterface.class);
- if (enableUnusedInterfaceRemoval) {
- assertThat(subInterface, not(isPresent()));
- } else {
- assertThat(subInterface, isPresent());
- assertThat(subInterface, isPresentAndRenamed());
- }
+ assertThat(
+ subInterface, isAbsentIf(enableUnusedInterfaceRemoval || enableVerticalClassMerging));
}
@Test