Merge commit '1ebd2af288f7ff4ab8f22a569907a72cd8d4fe2f' into dev-release
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index d0cc5af..79d8121 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -67,10 +67,10 @@
import com.android.tools.r8.optimize.MemberRebindingAnalysis;
import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
import com.android.tools.r8.optimize.MemberRebindingIdentityLensFactory;
-import com.android.tools.r8.optimize.RedundantBridgeRemover;
import com.android.tools.r8.optimize.bridgehoisting.BridgeHoisting;
import com.android.tools.r8.optimize.interfaces.analysis.CfOpenClosedInterfacesAnalysis;
import com.android.tools.r8.optimize.proto.ProtoNormalizer;
+import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemover;
import com.android.tools.r8.origin.CommandLineOrigin;
import com.android.tools.r8.profile.art.ArtProfileCompletenessChecker;
import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 97ceb6e..a664d9d 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -747,7 +747,7 @@
dataResourceConsumer.accept(adapted, options.reporter);
} else {
options.reporter.warning(
- new StringDiagnostic("Resource '" + adapted.getName() + "' already exists."));
+ new StringDiagnostic("Resource '" + file.getName() + "' already exists."));
}
options.reporter.failIfPendingErrors();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index f30986c..f9a5279 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -475,9 +475,9 @@
return isInstanceInitializer() || willBeInlinedIntoInstanceInitializer(dexItemFactory);
}
- public boolean isDefaultInitializer() {
+ public boolean isDefaultInstanceInitializer() {
checkIfObsolete();
- return isInstanceInitializer() && getReference().proto.parameters.isEmpty();
+ return isInstanceInitializer() && getParameters().isEmpty();
}
public boolean isClassInitializer() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 13c5ab8..4d6f31a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -602,6 +602,9 @@
public final DexType javaUtilSetType = createStaticallyKnownType("Ljava/util/Set;");
public final DexType androidAppActivity = createStaticallyKnownType("Landroid/app/Activity;");
+ public final DexType androidAppFragment = createStaticallyKnownType("Landroid/app/Fragment;");
+ public final DexType androidAppZygotePreload =
+ createStaticallyKnownType("Landroid/app/ZygotePreload;");
public final DexType androidOsBuildType = createStaticallyKnownType("Landroid/os/Build;");
public final DexType androidOsBuildVersionType =
createStaticallyKnownType("Landroid/os/Build$VERSION;");
@@ -1182,7 +1185,7 @@
// android.content.ContentProviderClient
public class AndroidContentContentProviderClientMembers extends LibraryMembers {
public final DexMethod release =
- createMethod(androidContentContentProviderClientType, createProto(voidType), "release");
+ createMethod(androidContentContentProviderClientType, createProto(booleanType), "release");
public final DexMethod close =
createMethod(androidContentContentProviderClientType, createProto(voidType), "close");
}
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 bba2edd..9117bcc 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java
@@ -39,6 +39,34 @@
AppView<? extends AppInfoWithClassHierarchy> appView,
IRCodeProvider codeProvider,
MergeGroup group,
+ InstanceInitializer instanceInitializer) {
+ if (instanceInitializer.isAbsent()) {
+ InstanceInitializerDescription.Builder builder =
+ InstanceInitializerDescription.builder(appView, instanceInitializer.getReference());
+ DexMethod invokedConstructor =
+ instanceInitializer
+ .getReference()
+ .withHolder(group.getSuperType(), appView.dexItemFactory());
+ List<InstanceFieldInitializationInfo> invokedConstructorArguments = new ArrayList<>();
+ for (int argumentIndex = 1;
+ argumentIndex < invokedConstructor.getNumberOfArguments(false);
+ argumentIndex++) {
+ invokedConstructorArguments.add(
+ appView
+ .instanceFieldInitializationInfoFactory()
+ .createArgumentInitializationInfo(argumentIndex));
+ }
+ builder.addInvokeConstructor(invokedConstructor, invokedConstructorArguments);
+ return builder.build();
+ } else {
+ return analyze(appView, codeProvider, group, instanceInitializer.asPresent().getMethod());
+ }
+ }
+
+ public static InstanceInitializerDescription analyze(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ IRCodeProvider codeProvider,
+ MergeGroup group,
ProgramMethod instanceInitializer) {
InstanceInitializerDescription.Builder builder =
InstanceInitializerDescription.builder(appView, instanceInitializer);
@@ -179,4 +207,65 @@
private static InstanceInitializerDescription invalid() {
return null;
}
+
+ public abstract static class InstanceInitializer {
+
+ public abstract DexMethod getReference();
+
+ public abstract boolean isAbsent();
+
+ public abstract PresentInstanceInitializer asPresent();
+ }
+
+ public static class AbsentInstanceInitializer extends InstanceInitializer {
+
+ private final DexMethod methodReference;
+
+ public AbsentInstanceInitializer(DexMethod methodReference) {
+ this.methodReference = methodReference;
+ }
+
+ @Override
+ public DexMethod getReference() {
+ return methodReference;
+ }
+
+ @Override
+ public boolean isAbsent() {
+ return true;
+ }
+
+ @Override
+ public PresentInstanceInitializer asPresent() {
+ return null;
+ }
+ }
+
+ public static class PresentInstanceInitializer extends InstanceInitializer {
+
+ private final ProgramMethod method;
+
+ public PresentInstanceInitializer(ProgramMethod method) {
+ this.method = method;
+ }
+
+ public ProgramMethod getMethod() {
+ return method;
+ }
+
+ @Override
+ public DexMethod getReference() {
+ return method.getReference();
+ }
+
+ @Override
+ public boolean isAbsent() {
+ return false;
+ }
+
+ @Override
+ public PresentInstanceInitializer asPresent() {
+ return this;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
index 894c017..52a00c1 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
@@ -57,6 +57,11 @@
}
public static Builder builder(
+ AppView<? extends AppInfoWithClassHierarchy> appView, DexMethod instanceInitializer) {
+ return new Builder(appView.dexItemFactory(), instanceInitializer);
+ }
+
+ public static Builder builder(
AppView<? extends AppInfoWithClassHierarchy> appView, ProgramMethod instanceInitializer) {
return new Builder(appView.dexItemFactory(), instanceInitializer);
}
@@ -120,15 +125,19 @@
private DexMethod parentConstructor;
private List<InstanceFieldInitializationInfo> parentConstructorArguments;
- Builder(DexItemFactory dexItemFactory, ProgramMethod method) {
+ Builder(DexItemFactory dexItemFactory, DexMethod methodReference) {
this.dexItemFactory = dexItemFactory;
this.relaxedParameters =
- method
+ methodReference
.getParameters()
.map(
parameter -> parameter.isPrimitiveType() ? parameter : dexItemFactory.objectType);
}
+ Builder(DexItemFactory dexItemFactory, ProgramMethod method) {
+ this(dexItemFactory, method.getReference());
+ }
+
public void addInstancePut(DexField field, InstanceFieldInitializationInfo value) {
if (parentConstructor == null) {
instanceFieldAssignmentsPre.put(field, value);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java
index deb0e02..294d87d 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java
@@ -282,8 +282,7 @@
MapUtils.newIdentityHashMap(
builder ->
Iterables.filter(groups, MergeGroup::isClassGroup)
- .forEach(
- group -> group.forEach(clazz -> builder.accept(clazz.getType(), group))));
+ .forEach(group -> group.forEach(clazz -> builder.put(clazz.getType(), group))));
// Copy the map from classes to their inherited default methods.
Map<DexType, Map<DexMethodSignature, Set<DexMethod>>>
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceInitializerMerging.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceInitializerMerging.java
index 02b6bbd..4c07501 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceInitializerMerging.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceInitializerMerging.java
@@ -4,38 +4,47 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodSignature;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.MethodAccessInfoCollection;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.horizontalclassmerging.ClassInstanceFieldsMerger;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.IRCodeProvider;
import com.android.tools.r8.horizontalclassmerging.InstanceInitializerAnalysis;
+import com.android.tools.r8.horizontalclassmerging.InstanceInitializerAnalysis.AbsentInstanceInitializer;
+import com.android.tools.r8.horizontalclassmerging.InstanceInitializerAnalysis.InstanceInitializer;
+import com.android.tools.r8.horizontalclassmerging.InstanceInitializerAnalysis.PresentInstanceInitializer;
import com.android.tools.r8.horizontalclassmerging.InstanceInitializerDescription;
import com.android.tools.r8.horizontalclassmerging.MergeGroup;
-import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
+import com.android.tools.r8.horizontalclassmerging.MultiClassPolicyWithPreprocessing;
+import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.MapUtils;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap;
import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap;
-import com.android.tools.r8.utils.collections.ProgramMethodMap;
-import com.google.common.collect.Iterators;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
-import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.ExecutorService;
import java.util.function.Function;
/**
@@ -46,7 +55,8 @@
* <p>This policy requires that all instance initializers with the same signature (relaxed, by
* converting references types to java.lang.Object) have the same behavior.
*/
-public class NoInstanceInitializerMerging extends MultiClassPolicy {
+public class NoInstanceInitializerMerging
+ extends MultiClassPolicyWithPreprocessing<Map<DexProgramClass, Set<DexMethod>>> {
private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final IRCodeProvider codeProvider;
@@ -61,7 +71,53 @@
}
@Override
- public Collection<MergeGroup> apply(MergeGroup group) {
+ public Map<DexProgramClass, Set<DexMethod>> preprocess(
+ Collection<MergeGroup> groups, ExecutorService executorService) {
+ if (!appView.options().canHaveNonReboundConstructorInvoke()) {
+ return Collections.emptyMap();
+ }
+
+ if (appView.hasLiveness()) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ MethodAccessInfoCollection methodAccessInfoCollection =
+ appView.appInfoWithLiveness().getMethodAccessInfoCollection();
+
+ // Compute a mapping for the merge candidates to efficiently determine if a given type is a
+ // merge candidate.
+ Map<DexType, DexProgramClass> mergeCandidates =
+ MapUtils.newImmutableMap(
+ builder ->
+ IterableUtils.flatten(groups)
+ .forEach(
+ mergeCandidate -> builder.put(mergeCandidate.getType(), mergeCandidate)));
+
+ // Compute a mapping from merge candidates to the constructors that have been removed by
+ // constructor shrinking.
+ return MapUtils.newIdentityHashMap(
+ builder ->
+ methodAccessInfoCollection.forEachDirectInvoke(
+ (method, contexts) -> {
+ DexProgramClass mergeCandidateHolder =
+ mergeCandidates.get(method.getHolderType());
+ if (mergeCandidateHolder != null
+ && method.isInstanceInitializer(dexItemFactory)
+ && mergeCandidateHolder.getMethodCollection().getMethod(method) == null) {
+ builder
+ .computeIfAbsent(
+ mergeCandidateHolder, ignoreKey(Sets::newIdentityHashSet))
+ .add(method);
+ }
+ }));
+ }
+
+ // Constructor shrinking is disabled when shrinking is disabled.
+ assert !appView.options().isShrinking();
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Collection<MergeGroup> apply(
+ MergeGroup group, Map<DexProgramClass, Set<DexMethod>> absentInstanceInitializers) {
assert !group.hasTarget();
assert !group.hasInstanceFieldMap();
@@ -76,7 +132,10 @@
// reference parameters are converted to java.lang.Object), to ensure that merging will result
// in a simple renaming (specifically, we must not need to append null arguments to constructor
// calls due to constructor collisions).
- group.removeIf(this::hasMultipleInstanceInitializersWithSameRelaxedSignature);
+ group.removeIf(
+ clazz ->
+ hasMultipleInstanceInitializersWithSameRelaxedSignature(
+ clazz, absentInstanceInitializers));
if (group.isEmpty()) {
return Collections.emptyList();
@@ -87,14 +146,14 @@
group.selectTarget(appView);
group.selectInstanceFieldMap(appView);
- Map<MergeGroup, Map<DexMethodSignature, ProgramMethod>> newGroups = new LinkedHashMap<>();
+ Map<MergeGroup, Map<DexMethodSignature, InstanceInitializer>> newGroups = new LinkedHashMap<>();
// Caching of instance initializer descriptions, which are used to determine equivalence.
// TODO(b/181846319): Make this cache available to the instance initializer merger so that we
// don't reanalyze instance initializers.
- ProgramMethodMap<Optional<InstanceInitializerDescription>> instanceInitializerDescriptions =
- ProgramMethodMap.create();
- Function<ProgramMethod, Optional<InstanceInitializerDescription>>
+ Map<DexMethod, Optional<InstanceInitializerDescription>> instanceInitializerDescriptions =
+ new IdentityHashMap<>();
+ Function<InstanceInitializer, Optional<InstanceInitializerDescription>>
instanceInitializerDescriptionProvider =
instanceInitializer ->
getOrComputeInstanceInitializerDescription(
@@ -104,11 +163,12 @@
// collisions.
for (DexProgramClass clazz : group) {
MergeGroup newGroup = null;
- Map<DexMethodSignature, ProgramMethod> classInstanceInitializers =
- getInstanceInitializersByRelaxedSignature(clazz);
- for (Entry<MergeGroup, Map<DexMethodSignature, ProgramMethod>> entry : newGroups.entrySet()) {
+ Map<DexMethodSignature, InstanceInitializer> classInstanceInitializers =
+ getInstanceInitializersByRelaxedSignature(clazz, absentInstanceInitializers);
+ for (Entry<MergeGroup, Map<DexMethodSignature, InstanceInitializer>> entry :
+ newGroups.entrySet()) {
MergeGroup candidateGroup = entry.getKey();
- Map<DexMethodSignature, ProgramMethod> groupInstanceInitializers = entry.getValue();
+ Map<DexMethodSignature, InstanceInitializer> groupInstanceInitializers = entry.getValue();
if (canAddClassToGroup(
classInstanceInitializers,
groupInstanceInitializers,
@@ -132,14 +192,16 @@
}
private boolean canAddClassToGroup(
- Map<DexMethodSignature, ProgramMethod> classInstanceInitializers,
- Map<DexMethodSignature, ProgramMethod> groupInstanceInitializers,
- Function<ProgramMethod, Optional<InstanceInitializerDescription>>
+ Map<DexMethodSignature, InstanceInitializer> classInstanceInitializers,
+ Map<DexMethodSignature, InstanceInitializer> groupInstanceInitializers,
+ Function<InstanceInitializer, Optional<InstanceInitializerDescription>>
instanceInitializerDescriptionProvider) {
- for (Entry<DexMethodSignature, ProgramMethod> entry : classInstanceInitializers.entrySet()) {
+ for (Entry<DexMethodSignature, InstanceInitializer> entry :
+ classInstanceInitializers.entrySet()) {
DexMethodSignature relaxedSignature = entry.getKey();
- ProgramMethod classInstanceInitializer = entry.getValue();
- ProgramMethod groupInstanceInitializer = groupInstanceInitializers.get(relaxedSignature);
+ InstanceInitializer classInstanceInitializer = entry.getValue();
+ InstanceInitializer groupInstanceInitializer =
+ groupInstanceInitializers.get(relaxedSignature);
if (groupInstanceInitializer == null) {
continue;
}
@@ -160,31 +222,41 @@
return true;
}
- private boolean hasMultipleInstanceInitializersWithSameRelaxedSignature(DexProgramClass clazz) {
- Iterator<ProgramMethod> instanceInitializers = clazz.programInstanceInitializers().iterator();
- if (!instanceInitializers.hasNext()) {
- // No instance initializers.
+ private boolean hasMultipleInstanceInitializersWithSameRelaxedSignature(
+ DexProgramClass clazz, Map<DexProgramClass, Set<DexMethod>> absentInstanceInitializers) {
+ Set<DexMethod> instanceInitializerReferences =
+ SetUtils.unionIdentityHashSet(
+ SetUtils.newIdentityHashSet(
+ IterableUtils.transform(
+ clazz.programInstanceInitializers(), ProgramMethod::getReference)),
+ absentInstanceInitializers.getOrDefault(clazz, Collections.emptySet()));
+ if (instanceInitializerReferences.size() <= 1) {
return false;
}
- ProgramMethod first = instanceInitializers.next();
- if (!instanceInitializers.hasNext()) {
- // Only a single instance initializer.
- return false;
- }
-
- Set<DexMethod> seen = SetUtils.newIdentityHashSet(getRelaxedSignature(first));
- return Iterators.any(
- instanceInitializers,
- instanceInitializer -> !seen.add(getRelaxedSignature(instanceInitializer)));
+ Set<DexMethod> seen = SetUtils.newIdentityHashSet();
+ return Iterables.any(
+ instanceInitializerReferences,
+ instanceInitializerReference ->
+ !seen.add(getRelaxedSignature(instanceInitializerReference)));
}
- private Map<DexMethodSignature, ProgramMethod> getInstanceInitializersByRelaxedSignature(
- DexProgramClass clazz) {
- Map<DexMethodSignature, ProgramMethod> result = new HashMap<>();
- for (ProgramMethod instanceInitializer : clazz.programInstanceInitializers()) {
- DexMethodSignature relaxedSignature = getRelaxedSignature(instanceInitializer).getSignature();
- ProgramMethod previous = result.put(relaxedSignature, instanceInitializer);
+ private Map<DexMethodSignature, InstanceInitializer> getInstanceInitializersByRelaxedSignature(
+ DexProgramClass clazz, Map<DexProgramClass, Set<DexMethod>> absentInstanceInitializers) {
+ Map<DexMethodSignature, InstanceInitializer> result = new HashMap<>();
+ for (ProgramMethod presentInstanceInitializer : clazz.programInstanceInitializers()) {
+ DexMethodSignature relaxedSignature =
+ getRelaxedSignature(presentInstanceInitializer).getSignature();
+ InstanceInitializer previous =
+ result.put(relaxedSignature, new PresentInstanceInitializer(presentInstanceInitializer));
+ assert previous == null;
+ }
+ for (DexMethod absentInstanceInitializer :
+ absentInstanceInitializers.getOrDefault(clazz, Collections.emptySet())) {
+ DexMethodSignature relaxedSignature =
+ getRelaxedSignature(absentInstanceInitializer).getSignature();
+ InstanceInitializer previous =
+ result.put(relaxedSignature, new AbsentInstanceInitializer(absentInstanceInitializer));
assert previous == null;
}
return result;
@@ -192,10 +264,10 @@
private Optional<InstanceInitializerDescription> getOrComputeInstanceInitializerDescription(
MergeGroup group,
- ProgramMethod instanceInitializer,
- ProgramMethodMap<Optional<InstanceInitializerDescription>> instanceInitializerDescriptions) {
+ InstanceInitializer instanceInitializer,
+ Map<DexMethod, Optional<InstanceInitializerDescription>> instanceInitializerDescriptions) {
return instanceInitializerDescriptions.computeIfAbsent(
- instanceInitializer,
+ instanceInitializer.getReference(),
key -> {
InstanceInitializerDescription instanceInitializerDescription =
InstanceInitializerAnalysis.analyze(
@@ -205,15 +277,20 @@
}
private DexMethod getRelaxedSignature(ProgramMethod instanceInitializer) {
+ return getRelaxedSignature(instanceInitializer.getReference());
+ }
+
+ private DexMethod getRelaxedSignature(DexMethod instanceInitializerReference) {
DexType objectType = appView.dexItemFactory().objectType;
- DexTypeList parameters = instanceInitializer.getParameters();
+ DexTypeList parameters = instanceInitializerReference.getParameters();
DexTypeList relaxedParameters =
parameters.map(parameter -> parameter.isPrimitiveType() ? parameter : objectType);
return parameters != relaxedParameters
? appView
.dexItemFactory()
- .createInstanceInitializer(instanceInitializer.getHolderType(), relaxedParameters)
- : instanceInitializer.getReference();
+ .createInstanceInitializer(
+ instanceInitializerReference.getHolderType(), relaxedParameters)
+ : instanceInitializerReference;
}
private void setInstanceFieldMaps(Iterable<MergeGroup> newGroups, MergeGroup group) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
index 637026f..d871616 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
-import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -70,7 +69,6 @@
IRCode code,
BytecodeMetadataProvider.Builder bytecodeMetadataProviderBuilder,
OptimizationFeedback feedback,
- FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis,
MethodProcessor methodProcessor) {
if (!methodProcessor.isPrimaryMethodProcessor()) {
return;
@@ -87,8 +85,7 @@
appView.appInfo().resolveField(fieldInstruction.getField()).getProgramField();
if (field != null) {
if (fieldAssignmentTracker != null) {
- fieldAssignmentTracker.recordFieldAccess(
- fieldInstruction, field, fieldReadBeforeWriteAnalysis);
+ fieldAssignmentTracker.recordFieldAccess(fieldInstruction, field, code.context());
}
if (fieldBitAccessAnalysis != null) {
fieldBitAccessAnalysis.recordFieldAccess(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
index dc90a85..0db5825 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -19,7 +19,6 @@
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerUtils;
-import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
import com.android.tools.r8.ir.analysis.fieldaccess.state.ConcreteArrayTypeFieldState;
import com.android.tools.r8.ir.analysis.fieldaccess.state.ConcreteClassTypeFieldState;
import com.android.tools.r8.ir.analysis.fieldaccess.state.ConcretePrimitiveTypeFieldState;
@@ -35,8 +34,6 @@
import com.android.tools.r8.ir.analysis.value.SingleValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.code.FieldInstruction;
-import com.android.tools.r8.ir.code.FieldPut;
-import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.Value;
@@ -182,23 +179,16 @@
});
}
- void recordFieldAccess(
- FieldInstruction instruction,
- ProgramField field,
- FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
+ void recordFieldAccess(FieldInstruction instruction, ProgramField field, ProgramMethod context) {
if (instruction.isFieldPut()) {
- recordFieldPut(instruction.asFieldPut(), field, fieldReadBeforeWriteAnalysis);
+ recordFieldPut(field, instruction.value(), context);
}
}
- private void recordFieldPut(
- FieldPut fieldPut,
- ProgramField field,
- FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
+ private void recordFieldPut(ProgramField field, Value value, ProgramMethod context) {
// For now only attempt to prove that fields are definitely null. In order to prove a single
// value for fields that are not definitely null, we need to prove that the given field is never
// read before it is written.
- Value value = fieldPut.value();
AbstractValue abstractValue =
value.isZero() ? abstractValueFactory.createZeroValue() : AbstractValue.unknown();
fieldStates.compute(
@@ -213,8 +203,12 @@
return ConcretePrimitiveTypeFieldState.create(abstractValue);
}
assert fieldType.isClassType();
- return ConcreteClassTypeFieldState.create(
- abstractValue, getDynamicType(fieldPut, field, fieldReadBeforeWriteAnalysis));
+ DynamicType dynamicType =
+ WideningUtils.widenDynamicNonReceiverType(
+ appView,
+ value.getDynamicType(appView).withNullability(Nullability.maybeNull()),
+ field.getType());
+ return ConcreteClassTypeFieldState.create(abstractValue, dynamicType);
}
if (fieldState.isUnknown()) {
@@ -237,31 +231,10 @@
ConcreteClassTypeFieldState classFieldState = fieldState.asClass();
return classFieldState.mutableJoin(
- appView,
- abstractValue,
- getDynamicType(fieldPut, field, fieldReadBeforeWriteAnalysis),
- field);
+ appView, abstractValue, value.getDynamicType(appView), field);
});
}
- private DynamicType getDynamicType(
- FieldPut fieldPut,
- ProgramField field,
- FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
- DynamicTypeWithUpperBound dynamicType = fieldPut.value().getDynamicType(appView);
- if (fieldPut.isInstancePut()) {
- InstancePut instancePut = fieldPut.asInstancePut();
- if (fieldReadBeforeWriteAnalysis.isInstanceFieldMaybeReadBeforeInstruction(
- instancePut.object(), field.getDefinition(), instancePut)) {
- dynamicType = dynamicType.withNullability(Nullability.maybeNull());
- }
- } else if (fieldReadBeforeWriteAnalysis.isStaticFieldMaybeReadBeforeInstruction(
- field.getDefinition(), fieldPut.asStaticPut())) {
- dynamicType = dynamicType.withNullability(Nullability.maybeNull());
- }
- return WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, field.getType());
- }
-
void recordAllocationSite(NewInstance instruction, DexProgramClass clazz, ProgramMethod context) {
Map<DexEncodedField, AbstractValue> abstractInstanceFieldValuesForClass =
abstractInstanceFieldValues.get(clazz);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysis.java
deleted file mode 100644
index fb6bae0..0000000
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysis.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// 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.ir.analysis.fieldaccess.readbeforewrite;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.Value;
-
-public abstract class FieldReadBeforeWriteAnalysis {
-
- public static FieldReadBeforeWriteAnalysis create(
- AppView<?> appView, IRCode code, ProgramMethod context) {
- if (appView.hasLiveness()) {
- return new FieldReadBeforeWriteAnalysisImpl(appView.withLiveness(), code, context);
- }
- return trivial();
- }
-
- public static TrivialFieldReadBeforeWriteAnalysis trivial() {
- return new TrivialFieldReadBeforeWriteAnalysis();
- }
-
- public abstract boolean isInstanceFieldMaybeReadBeforeInstruction(
- Value receiver, DexEncodedField field, Instruction instruction);
-
- public abstract boolean isStaticFieldMaybeReadBeforeInstruction(
- DexEncodedField field, Instruction instruction);
-}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysisImpl.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysisImpl.java
deleted file mode 100644
index 7e7ac89..0000000
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysisImpl.java
+++ /dev/null
@@ -1,209 +0,0 @@
-// 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.ir.analysis.fieldaccess.readbeforewrite;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
-import com.android.tools.r8.ir.analysis.fieldvalueanalysis.ConcreteMutableFieldSet;
-import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
-import com.android.tools.r8.ir.analysis.fieldvalueanalysis.KnownFieldSet;
-import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InstructionIterator;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.DequeUtils;
-import java.util.Deque;
-import java.util.IdentityHashMap;
-import java.util.Map;
-
-class FieldReadBeforeWriteAnalysisImpl extends FieldReadBeforeWriteAnalysis {
-
- private final AppView<AppInfoWithLiveness> appView;
- private final IRCode code;
- private final ProgramMethod context;
-
- private Map<BasicBlock, AbstractFieldSet> fieldsMaybeReadBeforeBlockInclusiveCache;
-
- public FieldReadBeforeWriteAnalysisImpl(
- AppView<AppInfoWithLiveness> appView, IRCode code, ProgramMethod context) {
- this.appView = appView;
- this.code = code;
- this.context = context;
- }
-
- @Override
- public boolean isInstanceFieldMaybeReadBeforeInstruction(
- Value receiver, DexEncodedField field, Instruction instruction) {
- if (!code.context().getDefinition().isInstanceInitializer()
- || receiver.getAliasedValue() != code.getThis()) {
- return true;
- }
- return isFieldMaybeReadBeforeInstructionInInitializer(field, instruction);
- }
-
- @Override
- public boolean isStaticFieldMaybeReadBeforeInstruction(
- DexEncodedField field, Instruction instruction) {
- if (!code.context().getDefinition().isClassInitializer()
- || field.getHolderType() != code.context().getHolderType()) {
- return true;
- }
- return isFieldMaybeReadBeforeInstructionInInitializer(field, instruction);
- }
-
- public boolean isFieldMaybeReadBeforeInstructionInInitializer(
- DexEncodedField field, Instruction instruction) {
- BasicBlock block = instruction.getBlock();
-
- // First check if the field may be read in any of the (transitive) predecessor blocks.
- if (fieldMaybeReadBeforeBlock(field, block)) {
- return true;
- }
-
- // Then check if any of the instructions that precede the given instruction in the current block
- // may read the field.
- InstructionIterator instructionIterator = block.iterator();
- while (instructionIterator.hasNext()) {
- Instruction current = instructionIterator.next();
- if (current == instruction) {
- break;
- }
- if (current.readSet(appView, context).contains(field)) {
- return true;
- }
- }
-
- // Otherwise, the field is not read prior to the given instruction.
- return false;
- }
-
- private boolean fieldMaybeReadBeforeBlock(DexEncodedField field, BasicBlock block) {
- for (BasicBlock predecessor : block.getPredecessors()) {
- if (fieldMaybeReadBeforeBlockInclusive(field, predecessor)) {
- return true;
- }
- }
- return false;
- }
-
- private boolean fieldMaybeReadBeforeBlockInclusive(DexEncodedField field, BasicBlock block) {
- return getOrCreateFieldsMaybeReadBeforeBlockInclusive().get(block).contains(field);
- }
-
- private Map<BasicBlock, AbstractFieldSet> getOrCreateFieldsMaybeReadBeforeBlockInclusive() {
- if (fieldsMaybeReadBeforeBlockInclusiveCache == null) {
- fieldsMaybeReadBeforeBlockInclusiveCache = createFieldsMaybeReadBeforeBlockInclusive();
- }
- return fieldsMaybeReadBeforeBlockInclusiveCache;
- }
-
- /**
- * Eagerly creates a mapping from each block to the set of fields that may be read in that block
- * and its transitive predecessors.
- */
- private Map<BasicBlock, AbstractFieldSet> createFieldsMaybeReadBeforeBlockInclusive() {
- Map<BasicBlock, AbstractFieldSet> result = new IdentityHashMap<>();
- Deque<BasicBlock> worklist = DequeUtils.newArrayDeque(code.entryBlock());
- while (!worklist.isEmpty()) {
- BasicBlock block = worklist.removeFirst();
- boolean seenBefore = result.containsKey(block);
- AbstractFieldSet readSet =
- result.computeIfAbsent(block, ignore -> EmptyFieldSet.getInstance());
- if (readSet.isTop()) {
- // We already have unknown information for this block.
- continue;
- }
-
- assert readSet.isKnownFieldSet();
- KnownFieldSet knownReadSet = readSet.asKnownFieldSet();
- int oldSize = seenBefore ? knownReadSet.size() : -1;
-
- // Everything that is read in the predecessor blocks should also be included in the read set
- // for the current block, so here we join the information from the predecessor blocks into the
- // current read set.
- boolean blockOrPredecessorMaybeReadAnyField = false;
- for (BasicBlock predecessor : block.getPredecessors()) {
- AbstractFieldSet predecessorReadSet =
- result.getOrDefault(predecessor, EmptyFieldSet.getInstance());
- if (predecessorReadSet.isBottom()) {
- continue;
- }
- if (predecessorReadSet.isTop()) {
- blockOrPredecessorMaybeReadAnyField = true;
- break;
- }
- assert predecessorReadSet.isConcreteFieldSet();
- if (!knownReadSet.isConcreteFieldSet()) {
- knownReadSet = new ConcreteMutableFieldSet();
- }
- knownReadSet.asConcreteFieldSet().addAll(predecessorReadSet.asConcreteFieldSet());
- }
-
- if (!blockOrPredecessorMaybeReadAnyField) {
- // Finally, we update the read set with the fields that are read by the instructions in the
- // current block. This can be skipped if the block has already been processed.
- if (seenBefore) {
- assert verifyFieldSetContainsAllFieldReadsInBlock(knownReadSet, block, context);
- } else {
- for (Instruction instruction : block.getInstructions()) {
- AbstractFieldSet instructionReadSet = instruction.readSet(appView, context);
- if (instructionReadSet.isBottom()) {
- continue;
- }
- if (instructionReadSet.isTop()) {
- blockOrPredecessorMaybeReadAnyField = true;
- break;
- }
- if (!knownReadSet.isConcreteFieldSet()) {
- knownReadSet = new ConcreteMutableFieldSet();
- }
- knownReadSet.asConcreteFieldSet().addAll(instructionReadSet.asConcreteFieldSet());
- }
- }
- }
-
- boolean changed = false;
- if (blockOrPredecessorMaybeReadAnyField) {
- // Record that this block reads all fields.
- result.put(block, UnknownFieldSet.getInstance());
- changed = true;
- } else {
- if (knownReadSet != readSet) {
- result.put(block, knownReadSet.asConcreteFieldSet());
- }
- if (knownReadSet.size() != oldSize) {
- assert knownReadSet.size() > oldSize;
- changed = true;
- }
- }
-
- if (changed) {
- // Rerun the analysis for all successors because the state of the current block changed.
- worklist.addAll(block.getSuccessors());
- }
- }
- return result;
- }
-
- private boolean verifyFieldSetContainsAllFieldReadsInBlock(
- KnownFieldSet readSet, BasicBlock block, ProgramMethod context) {
- for (Instruction instruction : block.getInstructions()) {
- AbstractFieldSet instructionReadSet = instruction.readSet(appView, context);
- assert !instructionReadSet.isTop();
- if (instructionReadSet.isBottom()) {
- continue;
- }
- for (DexEncodedField field : instructionReadSet.asConcreteFieldSet().getFields()) {
- assert readSet.contains(field);
- }
- }
- return true;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/TrivialFieldReadBeforeWriteAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/TrivialFieldReadBeforeWriteAnalysis.java
deleted file mode 100644
index 9cd76dc..0000000
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/TrivialFieldReadBeforeWriteAnalysis.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// 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.ir.analysis.fieldaccess.readbeforewrite;
-
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.Value;
-
-class TrivialFieldReadBeforeWriteAnalysis extends FieldReadBeforeWriteAnalysis {
-
- @Override
- public boolean isInstanceFieldMaybeReadBeforeInstruction(
- Value receiver, DexEncodedField field, Instruction instruction) {
- return true;
- }
-
- @Override
- public boolean isStaticFieldMaybeReadBeforeInstruction(
- DexEncodedField field, Instruction instruction) {
- return true;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
index 854d046..b18d02a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
@@ -39,7 +39,7 @@
return this;
}
- public Set<DexEncodedField> getFields() {
+ Set<DexEncodedField> getFields() {
if (InternalOptions.assertionsEnabled()) {
return Collections.unmodifiableSet(fields);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
index 50c075e..9c09ef2 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
@@ -10,14 +10,13 @@
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.DominatorTree;
import com.android.tools.r8.ir.code.DominatorTree.Assumption;
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization.ClassInitializerDefaultsResult;
@@ -25,11 +24,14 @@
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo;
import com.android.tools.r8.ir.optimize.info.field.UnknownInstanceFieldInitializationInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.DequeUtils;
import com.android.tools.r8.utils.ListUtils;
import java.util.ArrayList;
+import java.util.Deque;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
public abstract class FieldValueAnalysis {
@@ -51,20 +53,16 @@
final OptimizationFeedback feedback;
private DominatorTree dominatorTree;
- private FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis;
+ private Map<BasicBlock, AbstractFieldSet> fieldsMaybeReadBeforeBlockInclusiveCache;
final Map<DexEncodedField, List<FieldInitializationInfo>> putsPerField = new IdentityHashMap<>();
FieldValueAnalysis(
- AppView<AppInfoWithLiveness> appView,
- IRCode code,
- OptimizationFeedback feedback,
- FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
+ AppView<AppInfoWithLiveness> appView, IRCode code, OptimizationFeedback feedback) {
this.appView = appView;
this.code = code;
- this.context = code.context();
this.feedback = feedback;
- this.fieldReadBeforeWriteAnalysis = fieldReadBeforeWriteAnalysis;
+ this.context = code.context();
}
DominatorTree getOrCreateDominatorTree() {
@@ -74,6 +72,13 @@
return dominatorTree;
}
+ private Map<BasicBlock, AbstractFieldSet> getOrCreateFieldsMaybeReadBeforeBlockInclusive() {
+ if (fieldsMaybeReadBeforeBlockInclusiveCache == null) {
+ fieldsMaybeReadBeforeBlockInclusiveCache = createFieldsMaybeReadBeforeBlockInclusive();
+ }
+ return fieldsMaybeReadBeforeBlockInclusiveCache;
+ }
+
boolean isInstanceFieldValueAnalysis() {
return false;
}
@@ -90,9 +95,9 @@
return null;
}
- abstract boolean isSubjectToOptimizationIgnoringPinning(ProgramField field);
+ abstract boolean isSubjectToOptimizationIgnoringPinning(DexEncodedField field);
- abstract boolean isSubjectToOptimization(ProgramField field);
+ abstract boolean isSubjectToOptimization(DexEncodedField field);
void recordFieldPut(DexEncodedField field, Instruction instruction) {
recordFieldPut(field, instruction, UnknownInstanceFieldInitializationInfo.getInstance());
@@ -122,12 +127,13 @@
DexField field = fieldPut.getField();
ProgramField programField = appInfo.resolveField(field).getProgramField();
if (programField != null) {
- if (isSubjectToOptimization(programField)) {
- recordFieldPut(programField.getDefinition(), fieldPut);
+ DexEncodedField encodedField = programField.getDefinition();
+ if (isSubjectToOptimization(encodedField)) {
+ recordFieldPut(encodedField, fieldPut);
} else if (isStaticFieldValueAnalysis()
&& programField.getHolder().isEnum()
- && isSubjectToOptimizationIgnoringPinning(programField)) {
- recordFieldPut(programField.getDefinition(), fieldPut);
+ && isSubjectToOptimizationIgnoringPinning(encodedField)) {
+ recordFieldPut(encodedField, fieldPut);
}
}
} else if (isInstanceFieldValueAnalysis()
@@ -138,54 +144,186 @@
}
}
- boolean checkDominance = !isStraightLineCode;
List<BasicBlock> normalExitBlocks = code.computeNormalExitBlocks();
- putsPerField.forEach(
- (field, fieldPuts) -> {
- if (fieldPuts.size() > 1) {
- return;
- }
- FieldInitializationInfo info = ListUtils.first(fieldPuts);
- Instruction instruction = info.instruction;
- if (instruction.isInvokeDirect()) {
- asInstanceFieldValueAnalysis()
- .recordInstanceFieldIsInitializedWithInfo(
- field, info.instanceFieldInitializationInfo);
- return;
- }
- FieldInstruction fieldPut = instruction.asFieldInstruction();
- if (checkDominance
- && !getOrCreateDominatorTree()
- .dominatesAllOf(fieldPut.getBlock(), normalExitBlocks)) {
- return;
- }
- boolean priorReadsWillReadSameValue =
- !classInitializerDefaultsResult.hasStaticValue(field) && fieldPut.value().isZero();
- if (!priorReadsWillReadSameValue) {
- if (fieldPut.isInstancePut()) {
- InstancePut instancePut = fieldPut.asInstancePut();
- if (fieldReadBeforeWriteAnalysis.isInstanceFieldMaybeReadBeforeInstruction(
- instancePut.object(), field, instancePut)) {
- return;
- }
- } else {
- if (fieldReadBeforeWriteAnalysis.isStaticFieldMaybeReadBeforeInstruction(
- field, fieldPut)) {
- // TODO(b/172528424): Generalize to InstanceFieldValueAnalysis.
- // At this point the value read in the field can be only the default static value,
- // if read prior to the put, or the value put, if read after the put. We still want
- // to record it because the default static value is typically null/0, so code
- // present after a null/0 check can take advantage of the optimization.
- DexValue valueBeforePut = classInitializerDefaultsResult.getStaticValue(field);
- asStaticFieldValueAnalysis()
- .updateFieldOptimizationInfoWith2Values(
- field, fieldPut.value(), valueBeforePut);
- return;
- }
+ for (Entry<DexEncodedField, List<FieldInitializationInfo>> entry : putsPerField.entrySet()) {
+ DexEncodedField field = entry.getKey();
+ List<FieldInitializationInfo> fieldPuts = entry.getValue();
+ if (fieldPuts.size() > 1) {
+ continue;
+ }
+ FieldInitializationInfo info = ListUtils.first(fieldPuts);
+ Instruction instruction = info.instruction;
+ if (instruction.isInvokeDirect()) {
+ asInstanceFieldValueAnalysis()
+ .recordInstanceFieldIsInitializedWithInfo(field, info.instanceFieldInitializationInfo);
+ continue;
+ }
+ FieldInstruction fieldPut = instruction.asFieldInstruction();
+ if (!isStraightLineCode) {
+ if (!getOrCreateDominatorTree().dominatesAllOf(fieldPut.getBlock(), normalExitBlocks)) {
+ continue;
+ }
+ }
+ boolean priorReadsWillReadSameValue =
+ !classInitializerDefaultsResult.hasStaticValue(field) && fieldPut.value().isZero();
+ if (!priorReadsWillReadSameValue && fieldMaybeReadBeforeInstruction(field, fieldPut)) {
+ // TODO(b/172528424): Generalize to InstanceFieldValueAnalysis.
+ if (isStaticFieldValueAnalysis()) {
+ // At this point the value read in the field can be only the default static value, if read
+ // prior to the put, or the value put, if read after the put. We still want to record it
+ // because the default static value is typically null/0, so code present after a null/0
+ // check can take advantage of the optimization.
+ DexValue valueBeforePut = classInitializerDefaultsResult.getStaticValue(field);
+ asStaticFieldValueAnalysis()
+ .updateFieldOptimizationInfoWith2Values(field, fieldPut.value(), valueBeforePut);
+ }
+ continue;
+ }
+ updateFieldOptimizationInfo(field, fieldPut, fieldPut.value());
+ }
+ }
+
+ private boolean fieldMaybeReadBeforeInstruction(
+ DexEncodedField encodedField, Instruction instruction) {
+ BasicBlock block = instruction.getBlock();
+
+ // First check if the field may be read in any of the (transitive) predecessor blocks.
+ if (fieldMaybeReadBeforeBlock(encodedField, block)) {
+ return true;
+ }
+
+ // Then check if any of the instructions that precede the given instruction in the current block
+ // may read the field.
+ InstructionIterator instructionIterator = block.iterator();
+ while (instructionIterator.hasNext()) {
+ Instruction current = instructionIterator.next();
+ if (current == instruction) {
+ break;
+ }
+ if (current.readSet(appView, context).contains(encodedField)) {
+ return true;
+ }
+ }
+
+ // Otherwise, the field is not read prior to the given instruction.
+ return false;
+ }
+
+ private boolean fieldMaybeReadBeforeBlock(DexEncodedField encodedField, BasicBlock block) {
+ for (BasicBlock predecessor : block.getPredecessors()) {
+ if (fieldMaybeReadBeforeBlockInclusive(encodedField, predecessor)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean fieldMaybeReadBeforeBlockInclusive(
+ DexEncodedField encodedField, BasicBlock block) {
+ return getOrCreateFieldsMaybeReadBeforeBlockInclusive().get(block).contains(encodedField);
+ }
+
+ /**
+ * Eagerly creates a mapping from each block to the set of fields that may be read in that block
+ * and its transitive predecessors.
+ */
+ private Map<BasicBlock, AbstractFieldSet> createFieldsMaybeReadBeforeBlockInclusive() {
+ Map<BasicBlock, AbstractFieldSet> result = new IdentityHashMap<>();
+ Deque<BasicBlock> worklist = DequeUtils.newArrayDeque(code.entryBlock());
+ while (!worklist.isEmpty()) {
+ BasicBlock block = worklist.removeFirst();
+ boolean seenBefore = result.containsKey(block);
+ AbstractFieldSet readSet =
+ result.computeIfAbsent(block, ignore -> EmptyFieldSet.getInstance());
+ if (readSet.isTop()) {
+ // We already have unknown information for this block.
+ continue;
+ }
+
+ assert readSet.isKnownFieldSet();
+ KnownFieldSet knownReadSet = readSet.asKnownFieldSet();
+ int oldSize = seenBefore ? knownReadSet.size() : -1;
+
+ // Everything that is read in the predecessor blocks should also be included in the read set
+ // for the current block, so here we join the information from the predecessor blocks into the
+ // current read set.
+ boolean blockOrPredecessorMaybeReadAnyField = false;
+ for (BasicBlock predecessor : block.getPredecessors()) {
+ AbstractFieldSet predecessorReadSet =
+ result.getOrDefault(predecessor, EmptyFieldSet.getInstance());
+ if (predecessorReadSet.isBottom()) {
+ continue;
+ }
+ if (predecessorReadSet.isTop()) {
+ blockOrPredecessorMaybeReadAnyField = true;
+ break;
+ }
+ assert predecessorReadSet.isConcreteFieldSet();
+ if (!knownReadSet.isConcreteFieldSet()) {
+ knownReadSet = new ConcreteMutableFieldSet();
+ }
+ knownReadSet.asConcreteFieldSet().addAll(predecessorReadSet.asConcreteFieldSet());
+ }
+
+ if (!blockOrPredecessorMaybeReadAnyField) {
+ // Finally, we update the read set with the fields that are read by the instructions in the
+ // current block. This can be skipped if the block has already been processed.
+ if (seenBefore) {
+ assert verifyFieldSetContainsAllFieldReadsInBlock(knownReadSet, block, context);
+ } else {
+ for (Instruction instruction : block.getInstructions()) {
+ AbstractFieldSet instructionReadSet = instruction.readSet(appView, context);
+ if (instructionReadSet.isBottom()) {
+ continue;
}
+ if (instructionReadSet.isTop()) {
+ blockOrPredecessorMaybeReadAnyField = true;
+ break;
+ }
+ if (!knownReadSet.isConcreteFieldSet()) {
+ knownReadSet = new ConcreteMutableFieldSet();
+ }
+ knownReadSet.asConcreteFieldSet().addAll(instructionReadSet.asConcreteFieldSet());
}
- updateFieldOptimizationInfo(field, fieldPut, fieldPut.value());
- });
+ }
+ }
+
+ boolean changed = false;
+ if (blockOrPredecessorMaybeReadAnyField) {
+ // Record that this block reads all fields.
+ result.put(block, UnknownFieldSet.getInstance());
+ changed = true;
+ } else {
+ if (knownReadSet != readSet) {
+ result.put(block, knownReadSet.asConcreteFieldSet());
+ }
+ if (knownReadSet.size() != oldSize) {
+ assert knownReadSet.size() > oldSize;
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ // Rerun the analysis for all successors because the state of the current block changed.
+ worklist.addAll(block.getSuccessors());
+ }
+ }
+ return result;
+ }
+
+ private boolean verifyFieldSetContainsAllFieldReadsInBlock(
+ KnownFieldSet readSet, BasicBlock block, ProgramMethod context) {
+ for (Instruction instruction : block.getInstructions()) {
+ AbstractFieldSet instructionReadSet = instruction.readSet(appView, context);
+ assert !instructionReadSet.isTop();
+ if (instructionReadSet.isBottom()) {
+ continue;
+ }
+ for (DexEncodedField field : instructionReadSet.asConcreteFieldSet().getFields()) {
+ assert readSet.contains(field);
+ }
+ }
+ return true;
}
abstract void updateFieldOptimizationInfo(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
index 9ae014f..c4061ec 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
@@ -11,9 +11,7 @@
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -52,10 +50,9 @@
AppView<AppInfoWithLiveness> appView,
IRCode code,
OptimizationFeedback feedback,
- FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis,
DexClassAndMethod parentConstructor,
InvokeDirect parentConstructorCall) {
- super(appView, code, feedback, fieldReadBeforeWriteAnalysis);
+ super(appView, code, feedback);
this.factory = appView.instanceFieldInitializationInfoFactory();
this.parentConstructor = parentConstructor;
this.parentConstructorCall = parentConstructorCall;
@@ -70,11 +67,10 @@
IRCode code,
ClassInitializerDefaultsResult classInitializerDefaultsResult,
OptimizationFeedback feedback,
- FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis,
Timing timing) {
timing.begin("Analyze instance initializer");
InstanceFieldInitializationInfoCollection result =
- run(appView, code, classInitializerDefaultsResult, feedback, fieldReadBeforeWriteAnalysis);
+ run(appView, code, classInitializerDefaultsResult, feedback);
timing.end();
return result;
}
@@ -83,8 +79,7 @@
AppView<?> appView,
IRCode code,
ClassInitializerDefaultsResult classInitializerDefaultsResult,
- OptimizationFeedback feedback,
- FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
+ OptimizationFeedback feedback) {
assert appView.appInfo().hasLiveness();
assert appView.enableWholeProgramOptimizations();
assert code.context().getDefinition().isInstanceInitializer();
@@ -106,7 +101,6 @@
appView.withLiveness(),
code,
feedback,
- fieldReadBeforeWriteAnalysis,
parentConstructor,
parentConstructorCall);
analysis.computeFieldOptimizationInfo(classInitializerDefaultsResult);
@@ -125,12 +119,12 @@
}
@Override
- boolean isSubjectToOptimization(ProgramField field) {
- return !field.getAccessFlags().isStatic() && field.getHolderType() == context.getHolderType();
+ boolean isSubjectToOptimization(DexEncodedField field) {
+ return !field.isStatic() && field.getHolderType() == context.getHolderType();
}
@Override
- boolean isSubjectToOptimizationIgnoringPinning(ProgramField field) {
+ boolean isSubjectToOptimizationIgnoringPinning(DexEncodedField field) {
throw new Unreachable("Used by static analysis only.");
}
@@ -209,7 +203,7 @@
if (abstractValue.isSingleValue()) {
return abstractValue.asSingleValue();
}
- DexType fieldType = field.getType();
+ DexType fieldType = field.type();
if (fieldType.isClassType()) {
ClassTypeElement dynamicLowerBoundType = value.getDynamicLowerBoundType(appView);
TypeElement dynamicUpperBoundType = value.getDynamicUpperBoundType(appView);
@@ -235,7 +229,7 @@
private boolean fieldNeverWrittenBetweenInstancePutAndMethodExit(
DexEncodedField field, InstancePut instancePut) {
- if (field.getAccessFlags().isFinal()) {
+ if (field.isFinal()) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
index 5f8381d..4df6b75 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -15,8 +15,6 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueNull;
-import com.android.tools.r8.graph.ProgramField;
-import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -51,11 +49,8 @@
private final Map<Value, AbstractValue> computedValues = new IdentityHashMap<>();
private StaticFieldValueAnalysis(
- AppView<AppInfoWithLiveness> appView,
- IRCode code,
- OptimizationFeedback feedback,
- FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) {
- super(appView, code, feedback, fieldReadBeforeWriteAnalysis);
+ AppView<AppInfoWithLiveness> appView, IRCode code, OptimizationFeedback feedback) {
+ super(appView, code, feedback);
builder = StaticFieldValues.builder(code.context().getHolder());
}
@@ -64,15 +59,13 @@
IRCode code,
ClassInitializerDefaultsResult classInitializerDefaultsResult,
OptimizationFeedback feedback,
- FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis,
Timing timing) {
assert appView.appInfo().hasLiveness();
assert appView.enableWholeProgramOptimizations();
assert code.context().getDefinition().isClassInitializer();
timing.begin("Analyze class initializer");
StaticFieldValues result =
- new StaticFieldValueAnalysis(
- appView.withLiveness(), code, feedback, fieldReadBeforeWriteAnalysis)
+ new StaticFieldValueAnalysis(appView.withLiveness(), code, feedback)
.analyze(classInitializerDefaultsResult);
timing.end();
return result;
@@ -124,22 +117,19 @@
}
@Override
- boolean isSubjectToOptimization(ProgramField field) {
- return field.getAccessFlags().isStatic()
+ boolean isSubjectToOptimization(DexEncodedField field) {
+ return field.isStatic()
&& field.getHolderType() == context.getHolderType()
- && appView
- .appInfo()
- .isFieldOnlyWrittenInMethod(field.getDefinition(), context.getDefinition());
+ && appView.appInfo().isFieldOnlyWrittenInMethod(field, context.getDefinition());
}
@Override
- boolean isSubjectToOptimizationIgnoringPinning(ProgramField field) {
- return field.getAccessFlags().isStatic()
+ boolean isSubjectToOptimizationIgnoringPinning(DexEncodedField field) {
+ return field.isStatic()
&& field.getHolderType() == context.getHolderType()
&& appView
.appInfo()
- .isFieldOnlyWrittenInMethodIgnoringPinning(
- field.getDefinition(), context.getDefinition());
+ .isFieldOnlyWrittenInMethodIgnoringPinning(field, context.getDefinition());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Monitor.java b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
index 3f5727b..025df81 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Monitor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.lightir.LirBuilder;
public class Monitor extends Instruction {
@@ -137,6 +138,11 @@
}
@Override
+ public void buildLir(LirBuilder<Value, ?> builder) {
+ builder.addMonitor(type, object());
+ }
+
+ @Override
public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, ProgramMethod context) {
return object() == value;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
index a829247..477bd14 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.lightir.LirBuilder;
public class NewArrayEmpty extends Instruction {
@@ -159,6 +160,11 @@
}
@Override
+ public void buildLir(LirBuilder<Value, ?> builder) {
+ builder.addNewArrayEmpty(size(), type);
+ }
+
+ @Override
public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
index 55ed123..72e5bc1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
@@ -26,6 +26,7 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.lightir.LirBuilder;
import java.util.Set;
public class NumberConversion extends Unop {
@@ -164,6 +165,11 @@
}
@Override
+ public void buildLir(LirBuilder<Value, ?> builder) {
+ builder.addNumberConversion(from, to, source());
+ }
+
+ @Override
public boolean outTypeKnownToBeBoolean(Set<Phi> seen) {
return to == NumericType.BYTE && source().knownToBeBoolean(seen);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index bc02268..fc8d1f2 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -18,7 +18,6 @@
import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
import com.android.tools.r8.ir.analysis.fieldaccess.FieldAccessAnalysis;
-import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.InstanceFieldValueAnalysis;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValueAnalysis;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues;
@@ -980,16 +979,10 @@
timing.end();
}
- FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis =
- FieldReadBeforeWriteAnalysis.create(appView, code, method);
if (fieldAccessAnalysis != null) {
timing.begin("Analyze field accesses");
fieldAccessAnalysis.recordFieldAccesses(
- code,
- bytecodeMetadataProviderBuilder,
- feedback,
- fieldReadBeforeWriteAnalysis,
- methodProcessor);
+ code, bytecodeMetadataProviderBuilder, feedback, methodProcessor);
if (classInitializerDefaultsResult != null) {
fieldAccessAnalysis.acceptClassInitializerDefaultsResult(classInitializerDefaultsResult);
}
@@ -1006,21 +999,11 @@
if (method.getDefinition().isClassInitializer()) {
staticFieldValues =
StaticFieldValueAnalysis.run(
- appView,
- code,
- classInitializerDefaultsResult,
- feedback,
- fieldReadBeforeWriteAnalysis,
- timing);
+ appView, code, classInitializerDefaultsResult, feedback, timing);
} else {
instanceFieldInitializationInfos =
InstanceFieldValueAnalysis.run(
- appView,
- code,
- classInitializerDefaultsResult,
- feedback,
- fieldReadBeforeWriteAnalysis,
- timing);
+ appView, code, classInitializerDefaultsResult, feedback, timing);
}
}
enumUnboxer.recordEnumState(method.getHolder(), staticFieldValues);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
index 125b53c..c2e13af 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
@@ -86,7 +86,7 @@
continue;
}
- if (method.getDefinition().isDefaultInitializer()
+ if (method.getDefinition().isDefaultInstanceInitializer()
&& appView.hasProguardCompatibilityActions()
&& appView.getProguardCompatibilityActions().isCompatInstantiated(method.getHolder())) {
continue;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/ContentProviderClientMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/ContentProviderClientMethodRewrites.java
index 8881483..5d1114b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/ContentProviderClientMethodRewrites.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/ContentProviderClientMethodRewrites.java
@@ -4,8 +4,16 @@
package com.android.tools.r8.ir.desugar.backports;
+import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.FullMethodInvokeRewriter;
import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
+import com.android.tools.r8.ir.desugar.LocalStackAllocator;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
import org.objectweb.asm.Opcodes;
public final class ContentProviderClientMethodRewrites {
@@ -15,10 +23,18 @@
public static MethodInvokeRewriter rewriteClose() {
// Rewrite android/content/ContentProviderClient#close to
// android/content/ContentProviderClient#recycle
- return (invoke, factory) ->
- new CfInvoke(
- Opcodes.INVOKEVIRTUAL,
- factory.androidContentContentProviderClientMembers.release,
- false);
+ return new FullMethodInvokeRewriter() {
+ @Override
+ public Collection<CfInstruction> rewrite(
+ CfInvoke invoke, DexItemFactory factory, LocalStackAllocator localStackAllocator) {
+ // The invoke consumes the stack value and pushes another assumed to be the same.
+ return ImmutableList.of(
+ new CfInvoke(
+ Opcodes.INVOKEVIRTUAL,
+ factory.androidContentContentProviderClientMembers.release,
+ false),
+ new CfStackInstruction(Opcode.Pop));
+ }
+ };
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index c92342f..f8e784e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -420,10 +420,9 @@
}
builder.setParent(invokedMethod);
} else {
- builder.markAllFieldsAsRead();
- if (invoke.instructionMayHaveSideEffects(appView, context)) {
- builder.setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
- }
+ builder
+ .markAllFieldsAsRead()
+ .setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
for (Value inValue : invoke.inValues()) {
if (couldBeReceiverValue(inValue, receiver, aliasesThroughAssumeAndCheckCasts)) {
builder.setReceiverMayEscapeOutsideConstructorChain();
@@ -454,10 +453,9 @@
case INVOKE_VIRTUAL:
{
InvokeMethod invoke = instruction.asInvokeMethod();
- builder.markAllFieldsAsRead();
- if (invoke.instructionMayHaveSideEffects(appView, context)) {
- builder.setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
- }
+ builder
+ .markAllFieldsAsRead()
+ .setMayHaveOtherSideEffectsThanInstanceFieldAssignments();
for (Value argument : invoke.arguments()) {
if (couldBeReceiverValue(argument, receiver, aliasesThroughAssumeAndCheckCasts)) {
builder.setReceiverMayEscapeOutsideConstructorChain();
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
index a9d515a..d63d8e7 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -95,7 +95,8 @@
Kotlin kotlin = appView.dexItemFactory().kotlin;
KotlinClassMetadata kMetadata = toKotlinClassMetadata(kotlin, annotation.annotation);
if (kMetadata == null) {
- throw new KotlinMetadataException();
+ throw new KotlinMetadataException(
+ new Exception("Could not parse metadata for " + clazz.toSourceString()));
}
return createKotlinInfo(kotlin, clazz, kMetadata, appView, keepByteCode);
}
diff --git a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
index 78e84db..70c442b 100644
--- a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
@@ -36,9 +36,13 @@
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.InvokeSuper;
import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.Monitor;
+import com.android.tools.r8.ir.code.MonitorType;
import com.android.tools.r8.ir.code.MoveException;
import com.android.tools.r8.ir.code.Mul;
+import com.android.tools.r8.ir.code.NewArrayEmpty;
import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.NumberConversion;
import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.Phi;
@@ -374,6 +378,14 @@
}
@Override
+ public void onNumberConversion(NumericType from, NumericType to, EV value) {
+ Value dest =
+ getOutValueForNextInstruction(
+ to.toDexType(appView.dexItemFactory()).toTypeElement(appView));
+ addInstruction(new NumberConversion(from, to, dest, getValue(value)));
+ }
+
+ @Override
public void onIf(IfType ifKind, int blockIndex, EV valueIndex) {
BasicBlock targetBlock = getBasicBlock(blockIndex);
Value value = getValue(valueIndex);
@@ -468,6 +480,12 @@
}
@Override
+ public void onNewArrayEmpty(DexType type, EV size) {
+ Value dest = getOutValueForNextInstruction(type.toTypeElement(appView));
+ addInstruction(new NewArrayEmpty(dest, getValue(size), type));
+ }
+
+ @Override
public void onThrow(EV exception) {
addInstruction(new Throw(getValue(exception)));
closeCurrentBlock();
@@ -550,5 +568,15 @@
Value dest = getOutValueForNextInstruction(TypeElement.getInt());
addInstruction(new Cmp(type, bias, dest, leftValue, rightValue));
}
+
+ @Override
+ public void onMonitorEnter(EV value) {
+ addInstruction(new Monitor(MonitorType.ENTER, getValue(value)));
+ }
+
+ @Override
+ public void onMonitorExit(EV value) {
+ addInstruction(new Monitor(MonitorType.EXIT, getValue(value)));
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
index 024d6d7..91ce188 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.cf.code.CfArithmeticBinop;
import com.android.tools.r8.cf.code.CfArithmeticBinop.Opcode;
+import com.android.tools.r8.cf.code.CfNumberConversion;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DebugLocalInfo;
@@ -21,6 +22,7 @@
import com.android.tools.r8.ir.code.Cmp.Bias;
import com.android.tools.r8.ir.code.IRMetadata;
import com.android.tools.r8.ir.code.IfType;
+import com.android.tools.r8.ir.code.MonitorType;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Position.SyntheticPosition;
@@ -325,21 +327,36 @@
}
public LirBuilder<V, EV> addDiv(NumericType type, V leftValue, V rightValue) {
+ int opcode;
switch (type) {
case BYTE:
case CHAR:
case SHORT:
case INT:
{
- return addInstructionTemplate(
- LirOpcodes.IDIV, Collections.emptyList(), ImmutableList.of(leftValue, rightValue));
+ opcode = LirOpcodes.IDIV;
+ break;
}
case LONG:
+ {
+ opcode = LirOpcodes.LDIV;
+ break;
+ }
case FLOAT:
+ {
+ opcode = LirOpcodes.FDIV;
+ break;
+ }
case DOUBLE:
+ {
+ opcode = LirOpcodes.DDIV;
+ break;
+ }
default:
- throw new Unimplemented();
+ throw new Unreachable("Unexpected type: " + type);
}
+ return addInstructionTemplate(
+ opcode, Collections.emptyList(), ImmutableList.of(leftValue, rightValue));
}
public LirBuilder<V, EV> addArrayLength(V array) {
@@ -399,7 +416,7 @@
}
public LirBuilder<V, EV> addReturn(V value) {
- throw new Unimplemented();
+ return addOneValueInstruction(LirOpcodes.ARETURN, value);
}
public LirBuilder<V, EV> addReturnVoid() {
@@ -556,4 +573,21 @@
int opcode = CfArithmeticBinop.getAsmOpcode(binop, type);
return addTwoValueInstruction(opcode, leftValue, rightValue);
}
+
+ public LirBuilder<V, EV> addMonitor(MonitorType type, V value) {
+ return addOneValueInstruction(
+ type == MonitorType.ENTER ? LirOpcodes.MONITORENTER : LirOpcodes.MONITOREXIT, value);
+ }
+
+ public LirBuilder<V, EV> addNewArrayEmpty(V size, DexType type) {
+ return addInstructionTemplate(
+ LirOpcodes.NEWARRAY, Collections.singletonList(type), Collections.singletonList(size));
+ }
+
+ public LirBuilder<V, EV> addNumberConversion(NumericType from, NumericType to, V value) {
+ int opcode = new CfNumberConversion(from, to).getAsmOpcode();
+ assert LirOpcodes.I2L <= opcode;
+ assert opcode <= LirOpcodes.I2S;
+ return addOneValueInstruction(opcode, value);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java b/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java
index a0300d2..302e85d 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java
@@ -148,11 +148,13 @@
// int RET = 169;
int TABLESWITCH = 170;
int LOOKUPSWITCH = 171;
- int IRETURN = 172;
- int LRETURN = 173;
- int FRETURN = 174;
- int DRETURN = 175;
+ // int IRETURN = 172;
+ // int LRETURN = 173;
+ // int FRETURN = 174;
+ // int DRETURN = 175;
+ // All value returns use areturn.
int ARETURN = 176;
+ // Void return.
int RETURN = 177;
int GETSTATIC = 178;
int PUTSTATIC = 179;
@@ -165,7 +167,8 @@
int INVOKEDYNAMIC = 186;
int NEW = 187;
int NEWARRAY = 188;
- int ANEWARRAY = 189;
+ // All arrays use NEWARRAY and a type item pointer
+ // int ANEWARRAY = 189;
int ARRAYLENGTH = 190;
int ATHROW = 191;
int CHECKCAST = 192;
@@ -429,14 +432,6 @@
return "TABLESWITCH";
case LOOKUPSWITCH:
return "LOOKUPSWITCH";
- case IRETURN:
- return "IRETURN";
- case LRETURN:
- return "LRETURN";
- case FRETURN:
- return "FRETURN";
- case DRETURN:
- return "DRETURN";
case ARETURN:
return "ARETURN";
case RETURN:
@@ -463,8 +458,6 @@
return "NEW";
case NEWARRAY:
return "NEWARRAY";
- case ANEWARRAY:
- return "ANEWARRAY";
case ARRAYLENGTH:
return "ARRAYLENGTH";
case ATHROW:
diff --git a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
index 925e8af..de3a7b4 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.lightir;
+import com.android.tools.r8.cf.code.CfNumberConversion;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItem;
@@ -178,6 +179,17 @@
onRem(NumericType.DOUBLE, leftValueIndex, rightValueIndex);
}
+ public void onNumberConversion(int opcode, EV value) {
+ assert LirOpcodes.I2L <= opcode;
+ assert opcode <= LirOpcodes.I2S;
+ CfNumberConversion insn = CfNumberConversion.fromAsm(opcode);
+ onNumberConversion(insn.getFromType(), insn.getToType(), value);
+ }
+
+ public void onNumberConversion(NumericType from, NumericType to, EV value) {
+ onInstruction();
+ }
+
public void onIf(IfType ifKind, int blockIndex, EV valueIndex) {
onInstruction();
}
@@ -238,6 +250,8 @@
public abstract void onInstancePut(DexField field, EV object, EV value);
+ public abstract void onNewArrayEmpty(DexType type, EV size);
+
public abstract void onThrow(EV exception);
public void onReturnVoid() {
@@ -261,6 +275,10 @@
onInstruction();
}
+ public abstract void onMonitorEnter(EV value);
+
+ public abstract void onMonitorExit(EV value);
+
private DexItem getConstantItem(int index) {
return code.getConstantItem(index);
}
@@ -487,6 +505,26 @@
onRemDouble(leftValueIndex, rightValueIndex);
return;
}
+ case LirOpcodes.I2L:
+ case LirOpcodes.I2F:
+ case LirOpcodes.I2D:
+ case LirOpcodes.L2I:
+ case LirOpcodes.L2F:
+ case LirOpcodes.L2D:
+ case LirOpcodes.F2I:
+ case LirOpcodes.F2L:
+ case LirOpcodes.F2D:
+ case LirOpcodes.D2I:
+ case LirOpcodes.D2L:
+ case LirOpcodes.D2F:
+ case LirOpcodes.I2B:
+ case LirOpcodes.I2C:
+ case LirOpcodes.I2S:
+ {
+ EV value = getNextValueOperand(view);
+ onNumberConversion(opcode, value);
+ return;
+ }
case LirOpcodes.IFNE:
{
int blockIndex = view.getNextBlockOperand();
@@ -568,6 +606,13 @@
onInstancePut(field, object, value);
return;
}
+ case LirOpcodes.NEWARRAY:
+ {
+ DexType type = (DexType) getConstantItem(view.getNextConstantOperand());
+ EV size = getNextValueOperand(view);
+ onNewArrayEmpty(type, size);
+ return;
+ }
case LirOpcodes.ATHROW:
{
EV exception = getNextValueOperand(view);
@@ -610,6 +655,18 @@
onMoveException(type);
return;
}
+ case LirOpcodes.MONITORENTER:
+ {
+ EV value = getNextValueOperand(view);
+ onMonitorEnter(value);
+ return;
+ }
+ case LirOpcodes.MONITOREXIT:
+ {
+ EV value = getNextValueOperand(view);
+ onMonitorExit(value);
+ return;
+ }
case LirOpcodes.DEBUGLOCALWRITE:
{
EV srcIndex = getNextValueOperand(view);
diff --git a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
index 965b6d7..19d5350 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
@@ -144,6 +144,12 @@
}
@Override
+ public void onNumberConversion(int opcode, EV value) {
+ appendOutValue();
+ appendValueArguments(value);
+ }
+
+ @Override
public void onIf(IfType ifKind, int blockIndex, EV valueIndex) {
builder.append(fmtValueIndex(valueIndex)).append(' ').append(fmtInsnIndex(blockIndex));
}
@@ -202,6 +208,13 @@
}
@Override
+ public void onNewArrayEmpty(DexType type, EV size) {
+ appendOutValue();
+ builder.append(type).append(' ');
+ appendValueArguments(size);
+ }
+
+ @Override
public void onThrow(EV exception) {
appendValueArguments(exception);
}
@@ -233,4 +246,14 @@
appendOutValue();
appendValueArguments(leftValue, rightValue);
}
+
+ @Override
+ public void onMonitorEnter(EV value) {
+ appendValueArguments(value);
+ }
+
+ @Override
+ public void onMonitorExit(EV value) {
+ appendValueArguments(value);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java
new file mode 100644
index 0000000..df58e45
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java
@@ -0,0 +1,52 @@
+// 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.optimize.redundantbridgeremoval;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexLibraryClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.SetUtils;
+import java.util.Collections;
+import java.util.Set;
+
+public class RedundantBridgeRemovalOptions {
+
+ private final InternalOptions options;
+
+ private boolean enableRetargetingOfConstructorBridgeCalls = false;
+ private Set<DexType> noConstructorShrinkingHierarchies;
+
+ public RedundantBridgeRemovalOptions(InternalOptions options) {
+ this.options = options;
+ }
+
+ public void clearNoConstructorShrinkingHierarchiesForTesting() {
+ noConstructorShrinkingHierarchies = Collections.emptySet();
+ }
+
+ public RedundantBridgeRemovalOptions ensureInitialized() {
+ if (noConstructorShrinkingHierarchies == null) {
+ DexItemFactory dexItemFactory = options.dexItemFactory();
+ noConstructorShrinkingHierarchies =
+ SetUtils.newIdentityHashSet(
+ dexItemFactory.androidAppFragment, dexItemFactory.androidAppZygotePreload);
+ }
+ return this;
+ }
+
+ public boolean isPlatformReflectingOnDefaultConstructorInSubclasses(DexLibraryClass clazz) {
+ return noConstructorShrinkingHierarchies.contains(clazz.getType());
+ }
+
+ public boolean isRetargetingOfConstructorBridgeCallsEnabled() {
+ return enableRetargetingOfConstructorBridgeCalls;
+ }
+
+ public void setEnableRetargetingOfConstructorBridgeCalls(
+ boolean enableRetargetingOfConstructorBridgeCalls) {
+ this.enableRetargetingOfConstructorBridgeCalls = enableRetargetingOfConstructorBridgeCalls;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
similarity index 72%
rename from src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
rename to src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
index 763c40f..49b5d17 100644
--- a/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
@@ -1,9 +1,10 @@
// Copyright (c) 2017, 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;
+package com.android.tools.r8.optimize.redundantbridgeremoval;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
@@ -15,23 +16,35 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
+import com.android.tools.r8.optimize.InvokeSingleTargetExtractor;
import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
-import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemovalLens;
+import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepMethodInfo;
+import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.WorkList;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.google.common.collect.Iterables;
+import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+import java.util.function.Consumer;
public class RedundantBridgeRemover {
private final AppView<AppInfoWithLiveness> appView;
+ private final RedundantBridgeRemovalOptions redundantBridgeRemovalOptions;
+
+ private final InvokedReflectivelyFromPlatformAnalysis invokedReflectivelyFromPlatformAnalysis =
+ new InvokedReflectivelyFromPlatformAnalysis();
public RedundantBridgeRemover(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
+ this.redundantBridgeRemovalOptions =
+ appView.options().getRedundantBridgeRemovalOptions().ensureInitialized();
}
private DexClassAndMethod getTargetForRedundantBridge(ProgramMethod method) {
@@ -52,6 +65,9 @@
if (!isTargetingSuperMethod(method, targetExtractor.getKind(), target)) {
return null;
}
+ if (invokedReflectivelyFromPlatformAnalysis.isMaybeInvokedReflectivelyFromPlatform(method)) {
+ return null;
+ }
// This is a visibility forward, so check for the direct target.
DexClassAndMethod targetMethod =
appView.appInfo().unsafeResolveMethodDueToDexFormatLegacy(target).getResolutionPair();
@@ -159,7 +175,7 @@
// TODO(b/245882297): Refine these visibility checks so that we also rewrite when
// the target is not public, but still accessible to call sites.
boolean isEligibleForRetargeting =
- appView.testing().enableRetargetingConstructorBridgeCalls
+ redundantBridgeRemovalOptions.isRetargetingOfConstructorBridgeCallsEnabled()
|| !method.getDefinition().isInstanceInitializer();
if (isEligibleForRetargeting
&& target.getAccessFlags().isPublic()
@@ -236,4 +252,88 @@
});
appView.pruneItems(prunedItemsBuilder.build(), executorService);
}
+
+ class InvokedReflectivelyFromPlatformAnalysis {
+
+ // Maps each class to a boolean indicating if the class inherits from android.app.Fragment or
+ // android.app.ZygotePreload.
+ private final Map<DexClass, Boolean> cache = new ConcurrentHashMap<>();
+
+ boolean isMaybeInvokedReflectivelyFromPlatform(ProgramMethod method) {
+ return method.getDefinition().isDefaultInstanceInitializer()
+ && !method.getHolder().isAbstract()
+ && computeIsPlatformReflectingOnDefaultConstructor(method.getHolder());
+ }
+
+ private boolean computeIsPlatformReflectingOnDefaultConstructor(DexProgramClass clazz) {
+ Boolean cacheResult = cache.get(clazz);
+ if (cacheResult != null) {
+ return cacheResult;
+ }
+ WorkList.<WorklistItem>newIdentityWorkList(new NotProcessedWorklistItem(clazz))
+ .process(WorklistItem::accept);
+ assert cache.containsKey(clazz);
+ return cache.get(clazz);
+ }
+
+ abstract class WorklistItem implements Consumer<WorkList<WorklistItem>> {
+
+ protected final DexClass clazz;
+
+ WorklistItem(DexClass clazz) {
+ this.clazz = clazz;
+ }
+
+ Iterable<DexClass> getImmediateSupertypes() {
+ return IterableUtils.flatMap(
+ clazz.allImmediateSupertypes(),
+ supertype -> {
+ DexClass definition = appView.definitionFor(supertype);
+ return definition != null
+ ? Collections.singletonList(definition)
+ : Collections.emptyList();
+ });
+ }
+ }
+
+ class NotProcessedWorklistItem extends WorklistItem {
+
+ NotProcessedWorklistItem(DexClass clazz) {
+ super(clazz);
+ }
+
+ @Override
+ public void accept(WorkList<WorklistItem> worklist) {
+ // Enqueue a worklist item to process the current class after processing its super classes.
+ worklist.addFirstIgnoringSeenSet(new ProcessedWorklistItem(clazz));
+ // Enqueue all superclasses for processing.
+ for (DexClass supertype : getImmediateSupertypes()) {
+ if (!cache.containsKey(supertype)) {
+ worklist.addFirstIgnoringSeenSet(new NotProcessedWorklistItem(supertype));
+ }
+ }
+ }
+ }
+
+ class ProcessedWorklistItem extends WorklistItem {
+
+ ProcessedWorklistItem(DexClass clazz) {
+ super(clazz);
+ }
+
+ @Override
+ public void accept(WorkList<WorklistItem> worklist) {
+ cache.put(
+ clazz,
+ Iterables.any(
+ getImmediateSupertypes(),
+ supertype ->
+ cache.get(supertype)
+ || (supertype.isLibraryClass()
+ && redundantBridgeRemovalOptions
+ .isPlatformReflectingOnDefaultConstructorInSubclasses(
+ supertype.asLibraryClass()))));
+ }
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java
index 3d2740a..a8cabe6 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -10,8 +10,6 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.Keep;
import com.android.tools.r8.Version;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.retrace.internal.ResultWithContextImpl;
import com.android.tools.r8.retrace.internal.RetraceAbortException;
import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy;
@@ -159,17 +157,17 @@
}
private final StackTraceLineParser<T, ST> stackTraceLineParser;
- private final StackTraceElementProxyRetracer<T, ST> proxyRetracer;
+ private final MappingSupplier<?> mappingSupplier;
private final DiagnosticsHandler diagnosticsHandler;
protected final boolean isVerbose;
Retrace(
StackTraceLineParser<T, ST> stackTraceLineParser,
- StackTraceElementProxyRetracer<T, ST> proxyRetracer,
+ MappingSupplier<?> mappingSupplier,
DiagnosticsHandler diagnosticsHandler,
boolean isVerbose) {
this.stackTraceLineParser = stackTraceLineParser;
- this.proxyRetracer = proxyRetracer;
+ this.mappingSupplier = mappingSupplier;
this.diagnosticsHandler = diagnosticsHandler;
this.isVerbose = isVerbose;
}
@@ -207,6 +205,10 @@
List<ST> stackTrace, RetraceStackTraceContext context) {
RetraceStackTraceElementProxyEquivalence<T, ST> equivalence =
new RetraceStackTraceElementProxyEquivalence<>(isVerbose);
+ stackTrace.forEach(proxy -> proxy.registerUses(mappingSupplier, diagnosticsHandler));
+ StackTraceElementProxyRetracer<T, ST> proxyRetracer =
+ StackTraceElementProxyRetracer.createDefault(
+ mappingSupplier.createRetracer(diagnosticsHandler));
List<List<List<T>>> finalResult = new ArrayList<>();
RetraceStackTraceContext finalContext =
ListUtils.fold(
@@ -260,6 +262,10 @@
Map<RetraceStackTraceElementProxy<T, ST>, List<T>> ambiguousBlocks = new HashMap<>();
List<RetraceStackTraceElementProxy<T, ST>> ambiguousKeys = new ArrayList<>();
ST parsedLine = stackTraceLineParser.parse(stackTraceFrame);
+ parsedLine.registerUses(mappingSupplier, diagnosticsHandler);
+ StackTraceElementProxyRetracer<T, ST> proxyRetracer =
+ StackTraceElementProxyRetracer.createDefault(
+ mappingSupplier.createRetracer(diagnosticsHandler));
Box<RetraceStackTraceContext> contextBox = new Box<>(context);
proxyRetracer.retrace(parsedLine, context).stream()
.forEach(
@@ -289,6 +295,10 @@
*/
public ResultWithContext<T> retraceLine(T stackTraceLine, RetraceStackTraceContext context) {
ST parsedLine = stackTraceLineParser.parse(stackTraceLine);
+ parsedLine.registerUses(mappingSupplier, diagnosticsHandler);
+ StackTraceElementProxyRetracer<T, ST> proxyRetracer =
+ StackTraceElementProxyRetracer.createDefault(
+ mappingSupplier.createRetracer(diagnosticsHandler));
Box<RetraceStackTraceContext> contextBox = new Box<>(context);
List<T> result =
proxyRetracer.retrace(parsedLine, context).stream()
@@ -335,31 +345,10 @@
lineNumber += 1;
}
timing.end();
- parsedStackTrace.forEach(
- proxy -> {
- if (proxy.hasClassName()) {
- mappingSupplier.registerClassUse(diagnosticsHandler, proxy.getClassReference());
- }
- if (proxy.hasMethodArguments()) {
- Arrays.stream(proxy.getMethodArguments().split(","))
- .forEach(
- typeName ->
- registerUseFromTypeReference(
- mappingSupplier, typeName, diagnosticsHandler));
- }
- if (proxy.hasFieldOrReturnType() && !proxy.getFieldOrReturnType().equals("void")) {
- registerUseFromTypeReference(
- mappingSupplier, proxy.getFieldOrReturnType(), diagnosticsHandler);
- }
- });
timing.begin("Read proguard map");
StringRetrace stringRetracer =
new StringRetrace(
- stackTraceLineParser,
- StackTraceElementProxyRetracer.createDefault(
- mappingSupplier.createRetracer(diagnosticsHandler)),
- diagnosticsHandler,
- options.isVerbose());
+ stackTraceLineParser, mappingSupplier, diagnosticsHandler, options.isVerbose());
timing.end();
timing.begin("Retracing");
ResultWithContext<String> result = stringRetracer.retraceParsed(parsedStackTrace, context);
@@ -389,17 +378,6 @@
}
}
- private static void registerUseFromTypeReference(
- MappingSupplier<?> mappingSupplier, String typeName, DiagnosticsHandler diagnosticsHandler) {
- TypeReference typeReference = Reference.typeFromTypeName(typeName);
- if (typeReference.isArray()) {
- typeReference = typeReference.asArray().getBaseType();
- }
- if (typeReference.isClass()) {
- mappingSupplier.registerClassUse(diagnosticsHandler, typeReference.asClass());
- }
- }
-
public static void run(String[] args) throws RetraceFailedException {
// To be compatible with standard retrace and remapper, we translate -arg into --arg.
String[] mappedArgs = new String[args.length];
@@ -502,7 +480,7 @@
public static class Builder<T, ST extends StackTraceElementProxy<T, ST>> {
private StackTraceLineParser<T, ST> stackTraceLineParser;
- private StackTraceElementProxyRetracer<T, ST> proxyRetracer;
+ private MappingSupplier<?> mappingSupplier;
private DiagnosticsHandler diagnosticsHandler;
protected boolean isVerbose;
@@ -512,12 +490,8 @@
return this;
}
- public Builder<T, ST> setRetracer(Retracer retracer) {
- return setProxyRetracer(StackTraceElementProxyRetracer.createDefault(retracer));
- }
-
- public Builder<T, ST> setProxyRetracer(StackTraceElementProxyRetracer<T, ST> proxyRetracer) {
- this.proxyRetracer = proxyRetracer;
+ public Builder<T, ST> setMappingSupplier(MappingSupplier<?> mappingSupplier) {
+ this.mappingSupplier = mappingSupplier;
return this;
}
@@ -532,7 +506,7 @@
}
public Retrace<T, ST> build() {
- return new Retrace<>(stackTraceLineParser, proxyRetracer, diagnosticsHandler, isVerbose);
+ return new Retrace<>(stackTraceLineParser, mappingSupplier, diagnosticsHandler, isVerbose);
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java b/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java
index 46c7eff..d18f748 100644
--- a/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java
+++ b/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java
@@ -4,8 +4,12 @@
package com.android.tools.r8.retrace;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.Keep;
import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.references.TypeReference;
+import java.util.Arrays;
@Keep
public abstract class StackTraceElementProxy<T, ST extends StackTraceElementProxy<T, ST>> {
@@ -40,4 +44,31 @@
public abstract T toRetracedItem(
RetraceStackTraceElementProxy<T, ST> retracedProxy, boolean verbose);
+
+ public void registerUses(
+ MappingSupplier<?> mappingSupplier, DiagnosticsHandler diagnosticsHandler) {
+ if (hasClassName()) {
+ mappingSupplier.registerClassUse(diagnosticsHandler, getClassReference());
+ }
+ if (hasMethodArguments()) {
+ Arrays.stream(getMethodArguments().split(","))
+ .forEach(
+ typeName ->
+ registerUseFromTypeReference(mappingSupplier, typeName, diagnosticsHandler));
+ }
+ if (hasFieldOrReturnType() && !getFieldOrReturnType().equals("void")) {
+ registerUseFromTypeReference(mappingSupplier, getFieldOrReturnType(), diagnosticsHandler);
+ }
+ }
+
+ private static void registerUseFromTypeReference(
+ MappingSupplier<?> mappingSupplier, String typeName, DiagnosticsHandler diagnosticsHandler) {
+ TypeReference typeReference = Reference.typeFromTypeName(typeName);
+ if (typeReference.isArray()) {
+ typeReference = typeReference.asArray().getBaseType();
+ }
+ if (typeReference.isClass()) {
+ mappingSupplier.registerClassUse(diagnosticsHandler, typeReference.asClass());
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/retrace/StringRetrace.java b/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
index 5a7a797..2810d8b 100644
--- a/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
@@ -27,10 +27,10 @@
StringRetrace(
StackTraceLineParser<String, StackTraceElementStringProxy> stackTraceLineParser,
- StackTraceElementProxyRetracer<String, StackTraceElementStringProxy> proxyRetracer,
+ MappingSupplier<?> mappingSupplier,
DiagnosticsHandler diagnosticsHandler,
boolean isVerbose) {
- super(stackTraceLineParser, proxyRetracer, diagnosticsHandler, isVerbose);
+ super(stackTraceLineParser, mappingSupplier, diagnosticsHandler, isVerbose);
}
/**
@@ -41,7 +41,7 @@
*/
public static StringRetrace create(RetraceOptions command) {
return create(
- command.getMappingSupplier().createRetracer(command.getDiagnosticsHandler()),
+ command.getMappingSupplier(),
command.getDiagnosticsHandler(),
command.getRegularExpression(),
command.isVerbose());
@@ -51,20 +51,20 @@
* Entry point for creating a retracer designed for string input and output where the mapping file
* has already been parsed.
*
- * @param retracer a loaded retracer with parsed mapping
+ * @param mappingSupplier a supplier that can be used to construct a retracer
* @param diagnosticsHandler a diagnosticshandler for emitting information
* @param regularExpression the regular expression to use for identifying information in strings
* @param isVerbose specify to emit verbose information
* @return a StringRetrace object
*/
public static StringRetrace create(
- Retracer retracer,
+ MappingSupplier<?> mappingSupplier,
DiagnosticsHandler diagnosticsHandler,
String regularExpression,
boolean isVerbose) {
return new StringRetrace(
StackTraceLineParser.createRegularExpressionParser(regularExpression),
- StackTraceElementProxyRetracer.createDefault(retracer),
+ mappingSupplier,
diagnosticsHandler,
isVerbose);
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapReaderWithFiltering.java b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapReaderWithFiltering.java
index 7c30dcd..282e28a 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapReaderWithFiltering.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapReaderWithFiltering.java
@@ -4,9 +4,6 @@
package com.android.tools.r8.retrace.internal;
-import static com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.LineParserState.COMPLETE_CLASS_MAPPING;
-import static com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.LineParserState.IS_COMMENT_SOURCE_FILE;
-import static com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.LineParserState.NOT_CLASS_MAPPING_OR_SOURCE_FILE;
import static java.lang.Integer.MAX_VALUE;
import com.android.tools.r8.errors.Unreachable;
@@ -23,20 +20,9 @@
public abstract class ProguardMapReaderWithFiltering implements LineReader {
- // The LineParserState encodes a simple state that the line parser can be in, where the
- // (successful) transitions allowed are:
+ private static final byte[] SOURCE_FILE_BYTES = "sourceFile".getBytes();
- // BEGINNING -> BEGINNING_NO_WHITESPACE -> SEEN_ORIGINAL_CLASS || IS_COMMENT_START
-
- // IS_COMMENT_START -> IS_COMMENT_SOURCE_FILE
-
- // SEEN_ORIGINAL_CLASS -> SEEN_ARROW -> SEEN_OBFUSCATED_CLASS -> COMPLETE_CLASS_MAPPING
- //
- // From all states there is a transition on invalid input to NOT_CLASS_MAPPING_OR_SOURCE_FILE.
- // The terminal states are:
- // { IS_COMMENT_SOURCE_FILE, COMPLETE_CLASS_MAPPING, NOT_CLASS_MAPPING_OR_SOURCE_FILE }
- //
- public enum LineParserState {
+ public enum LineParserNode {
BEGINNING,
BEGINNING_NO_WHITESPACE,
SEEN_ORIGINAL_CLASS,
@@ -52,62 +38,87 @@
|| this == COMPLETE_CLASS_MAPPING
|| this == IS_COMMENT_SOURCE_FILE;
}
+ }
- private static int currentIndex;
- private static int endIndex;
- private static byte[] bytes;
- private static final byte[] SOURCE_FILE_BYTES = "sourceFile".getBytes();
+ // The LineParserState encodes a simple state that the line parser can be in, where the
+ // (successful) transitions allowed are:
- public static LineParserState computeState(byte[] bytes, int startIndex, int endIndex) {
- currentIndex = startIndex;
- LineParserState.endIndex = endIndex;
- LineParserState.bytes = bytes;
- LineParserState currentState = BEGINNING;
- while (!currentState.isTerminal()) {
- currentState = currentState.computeNextState();
- }
- return currentState;
+ // BEGINNING -> BEGINNING_NO_WHITESPACE -> SEEN_ORIGINAL_CLASS || IS_COMMENT_START
+
+ // IS_COMMENT_START -> IS_COMMENT_SOURCE_FILE
+
+ // SEEN_ORIGINAL_CLASS -> SEEN_ARROW -> SEEN_OBFUSCATED_CLASS -> COMPLETE_CLASS_MAPPING
+ //
+ // From all states there is a transition on invalid input to NOT_CLASS_MAPPING_OR_SOURCE_FILE.
+ // The terminal states are:
+ // { IS_COMMENT_SOURCE_FILE, COMPLETE_CLASS_MAPPING, NOT_CLASS_MAPPING_OR_SOURCE_FILE }
+ //
+ private static class LineParserState {
+
+ private int currentIndex;
+ private final int endIndex;
+ private final byte[] bytes;
+ private LineParserNode node;
+
+ private LineParserState(byte[] bytes, int currentIndex, int endIndex) {
+ this.currentIndex = currentIndex;
+ this.endIndex = endIndex;
+ this.bytes = bytes;
+ node = LineParserNode.BEGINNING;
}
- private LineParserState computeNextState() {
- assert this != NOT_CLASS_MAPPING_OR_SOURCE_FILE;
- switch (this) {
+ private LineParserNode run() {
+ while (!node.isTerminal()) {
+ node = computeNextState();
+ }
+ return node;
+ }
+
+ private LineParserNode computeNextState() {
+ assert node != LineParserNode.NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+ switch (node) {
case BEGINNING:
return readUntilNoWhiteSpace()
- ? BEGINNING_NO_WHITESPACE
- : NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+ ? LineParserNode.BEGINNING_NO_WHITESPACE
+ : LineParserNode.NOT_CLASS_MAPPING_OR_SOURCE_FILE;
case BEGINNING_NO_WHITESPACE:
if (isCommentChar()) {
- return IS_COMMENT_START;
+ return LineParserNode.IS_COMMENT_START;
} else {
int readLength = readCharactersNoWhiteSpaceUntil(' ');
- return readLength > 0 ? SEEN_ORIGINAL_CLASS : NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+ return readLength > 0
+ ? LineParserNode.SEEN_ORIGINAL_CLASS
+ : LineParserNode.NOT_CLASS_MAPPING_OR_SOURCE_FILE;
}
case SEEN_ORIGINAL_CLASS:
- return readArrow() ? SEEN_ARROW : NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+ return readArrow()
+ ? LineParserNode.SEEN_ARROW
+ : LineParserNode.NOT_CLASS_MAPPING_OR_SOURCE_FILE;
case SEEN_ARROW:
int colonIndex = readCharactersNoWhiteSpaceUntil(':');
- return colonIndex > 0 ? SEEN_OBFUSCATED_CLASS : NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+ return colonIndex > 0
+ ? LineParserNode.SEEN_OBFUSCATED_CLASS
+ : LineParserNode.NOT_CLASS_MAPPING_OR_SOURCE_FILE;
case SEEN_OBFUSCATED_CLASS:
boolean read = readColon();
if (!read) {
- return NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+ return LineParserNode.NOT_CLASS_MAPPING_OR_SOURCE_FILE;
}
boolean noWhiteSpace = readUntilNoWhiteSpace();
return (!noWhiteSpace || isCommentChar())
- ? COMPLETE_CLASS_MAPPING
- : NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+ ? LineParserNode.COMPLETE_CLASS_MAPPING
+ : LineParserNode.NOT_CLASS_MAPPING_OR_SOURCE_FILE;
case IS_COMMENT_START:
if (readCharactersUntil('{')
&& readCharactersUntil(':')
&& readSingleOrDoubleQuote()
&& readSourceFile()) {
- return IS_COMMENT_SOURCE_FILE;
+ return LineParserNode.IS_COMMENT_SOURCE_FILE;
} else {
- return NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+ return LineParserNode.NOT_CLASS_MAPPING_OR_SOURCE_FILE;
}
default:
- assert isTerminal();
+ assert node.isTerminal();
throw new Unreachable("Should not compute next state on terminal state");
}
}
@@ -208,7 +219,7 @@
private boolean isInsideClassOfInterest = false;
private boolean seenFirstClass = false;
- private LineParserState lineParserState = NOT_CLASS_MAPPING_OR_SOURCE_FILE;
+ private LineParserNode lineParserResult = LineParserNode.NOT_CLASS_MAPPING_OR_SOURCE_FILE;
@Override
public String readLine() throws IOException {
@@ -220,8 +231,8 @@
if (filter == null) {
return new String(bytes, startIndex, endIndex - startIndex, StandardCharsets.UTF_8);
}
- lineParserState = LineParserState.computeState(bytes, startIndex, endIndex);
- if (lineParserState == COMPLETE_CLASS_MAPPING) {
+ lineParserResult = new LineParserState(bytes, startIndex, endIndex).run();
+ if (lineParserResult == LineParserNode.COMPLETE_CLASS_MAPPING) {
seenFirstClass = true;
String classMapping = getBufferAsString(bytes);
String obfuscatedClassName = getObfuscatedClassName(classMapping);
@@ -229,7 +240,8 @@
if (isInsideClassOfInterest || readPreambleAndSourceFiles) {
return classMapping;
}
- } else if (lineParserState == IS_COMMENT_SOURCE_FILE && readPreambleAndSourceFiles) {
+ } else if (lineParserResult == LineParserNode.IS_COMMENT_SOURCE_FILE
+ && readPreambleAndSourceFiles) {
return getBufferAsString(bytes);
} else if (isInsideClassOfInterest || (!seenFirstClass && readPreambleAndSourceFiles)) {
return getBufferAsString(bytes);
@@ -238,7 +250,7 @@
}
public boolean isClassMapping() {
- return lineParserState == COMPLETE_CLASS_MAPPING;
+ return lineParserResult == LineParserNode.COMPLETE_CLASS_MAPPING;
}
private String getBufferAsString(byte[] bytes) {
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 07a5bb2..39d8035 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -954,6 +954,14 @@
return staticInitializer != null && isFieldOnlyWrittenInMethod(field, staticInitializer);
}
+ public boolean mayPropagateArgumentsTo(ProgramMethod method) {
+ DexMethod reference = method.getReference();
+ return method.getDefinition().hasCode()
+ && !method.getDefinition().isLibraryMethodOverride().isPossiblyTrue()
+ && !neverReprocess.contains(reference)
+ && !keepInfo.getMethodInfo(method).isPinned(options());
+ }
+
public boolean mayPropagateValueFor(
AppView<AppInfoWithLiveness> appView, DexClassAndMember<?, ?> member) {
assert checkIfObsolete();
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 722a412..f48a387 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -74,6 +74,7 @@
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.MapVersion;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorEventConsumer;
+import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemovalOptions;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.profile.art.ArtProfileOptions;
@@ -889,6 +890,8 @@
private final OpenClosedInterfacesOptions openClosedInterfacesOptions =
new OpenClosedInterfacesOptions();
private final ProtoShrinkingOptions protoShrinking = new ProtoShrinkingOptions();
+ private final RedundantBridgeRemovalOptions redundantBridgeRemovalOptions =
+ new RedundantBridgeRemovalOptions(this);
private final KotlinOptimizationOptions kotlinOptimizationOptions =
new KotlinOptimizationOptions();
private final ApiModelTestingOptions apiModelTestingOptions = new ApiModelTestingOptions();
@@ -957,6 +960,10 @@
return cfCodeAnalysisOptions;
}
+ public RedundantBridgeRemovalOptions getRedundantBridgeRemovalOptions() {
+ return redundantBridgeRemovalOptions;
+ }
+
public DumpInputFlags getDumpInputFlags() {
return dumpInputFlags;
}
@@ -2141,7 +2148,6 @@
public boolean enableDeadSwitchCaseElimination = true;
public boolean enableInvokeSuperToInvokeVirtualRewriting = true;
public boolean enableMultiANewArrayDesugaringForClassFiles = false;
- public boolean enableRetargetingConstructorBridgeCalls = false;
public boolean enableSyntheticSharing = true;
public boolean enableSwitchToIfRewriting = true;
public boolean enableEnumUnboxingDebugLogs =
@@ -2917,8 +2923,7 @@
}
public boolean canHaveNonReboundConstructorInvoke() {
- // TODO(b/246679983): Turned off while diagnosing b/246679983.
- return false && isGeneratingDex() && minApiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.L);
+ return isGeneratingDex() && minApiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.L);
}
// b/238399429 Some art 6 vms have issues with multiple monitors in the same method
diff --git a/src/main/java/com/android/tools/r8/utils/MapUtils.java b/src/main/java/com/android/tools/r8/utils/MapUtils.java
index 7c603b2..5757218 100644
--- a/src/main/java/com/android/tools/r8/utils/MapUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MapUtils.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.utils;
import com.android.tools.r8.utils.StringUtils.BraceType;
+import com.google.common.collect.ImmutableMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
import java.util.Collections;
@@ -13,6 +14,7 @@
import java.util.Map.Entry;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
+import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Supplier;
@@ -41,9 +43,10 @@
return ignore -> supplier.get();
}
- public static <K, V> IdentityHashMap<K, V> newIdentityHashMap(BiForEachable<K, V> forEachable) {
+ public static <K, V> IdentityHashMap<K, V> newIdentityHashMap(
+ Consumer<IdentityHashMap<K, V>> builder) {
IdentityHashMap<K, V> map = new IdentityHashMap<>();
- forEachable.forEach(map::put);
+ builder.accept(map);
return map;
}
@@ -54,6 +57,13 @@
return map;
}
+ public static <K, V> ImmutableMap<K, V> newImmutableMap(
+ Consumer<ImmutableMap.Builder<K, V>> consumer) {
+ ImmutableMap.Builder<K, V> builder = ImmutableMap.builder();
+ consumer.accept(builder);
+ return builder.build();
+ }
+
public static <T> void removeIdentityMappings(Map<T, T> map) {
map.entrySet().removeIf(entry -> entry.getKey() == entry.getValue());
}
diff --git a/src/test/java/com/android/tools/r8/TestParameters.java b/src/test/java/com/android/tools/r8/TestParameters.java
index c22f43e..206a8ab 100644
--- a/src/test/java/com/android/tools/r8/TestParameters.java
+++ b/src/test/java/com/android/tools/r8/TestParameters.java
@@ -58,8 +58,7 @@
* runtime error.
*/
public boolean canHaveNonReboundConstructorInvoke() {
- // TODO(b/246679983): Turned off while diagnosing b/246679983.
- return false && isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L);
+ return isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L);
}
public boolean canUseDefaultAndStaticInterfaceMethods() {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java
index 70f55b4..4283b1a 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java
@@ -10,9 +10,10 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.AndroidApiLevel;
-import org.junit.Assume;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -23,36 +24,43 @@
public class HorizontalClassMergingAfterConstructorShrinkingTest extends TestBase {
@Parameter(0)
+ public boolean enableRetargetingOfConstructorBridgeCalls;
+
+ @Parameter(1)
public TestParameters parameters;
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters()
- .withDexRuntimes()
- .withApiLevelsStartingAtIncluding(AndroidApiLevel.L)
- .build();
+ @Parameters(name = "{1}, retarget: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(),
+ getTestParameters()
+ .withDexRuntimes()
+ .withApiLevelsStartingAtIncluding(AndroidApiLevel.L)
+ .build());
}
@Test
public void test() throws Exception {
- // TODO(b/276385221): Disabled constructor shrinking for now
- Assume.assumeTrue(parameters.canHaveNonReboundConstructorInvoke());
assertTrue(parameters.canHaveNonReboundConstructorInvoke());
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
.addOptionsModification(
+ options ->
+ options
+ .getRedundantBridgeRemovalOptions()
+ .setEnableRetargetingOfConstructorBridgeCalls(
+ enableRetargetingOfConstructorBridgeCalls))
+ .addOptionsModification(
options -> options.horizontalClassMergerOptions().disableInitialRoundOfClassMerging())
.addHorizontallyMergedClassesInspector(
- inspector ->
- inspector.assertIsCompleteMergeGroup(A.class, B.class).assertNoOtherClassesMerged())
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters)
.compile()
.run(parameters.getRuntime(), Main.class)
- // TODO(b/276385221): Should not trigger A.<init>.
- .assertSuccessWithOutputLines("Ouch!", "B");
+ .assertSuccessWithOutputLines("B");
}
static class Main {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingWithRepackagingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingWithRepackagingTest.java
new file mode 100644
index 0000000..e705e0c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingWithRepackagingTest.java
@@ -0,0 +1,180 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.KeepConstantArguments;
+import com.android.tools.r8.KeepUnusedArguments;
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * Similar to {@link HorizontalClassMergingAfterConstructorShrinkingTest}, but extended so that
+ * {@code B.<init>(Parent)} needs to be correctly lens rewritten in the horizontal class merger,
+ * since {@link Parent} is subject to repackaging, which runs prior to horizontal class merging.
+ */
+@RunWith(Parameterized.class)
+public class HorizontalClassMergingAfterConstructorShrinkingWithRepackagingTest extends TestBase {
+
+ @Parameter(0)
+ public boolean enableRetargetingOfConstructorBridgeCalls;
+
+ @Parameter(1)
+ public TestParameters parameters;
+
+ @Parameters(name = "{1}, retarget: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(),
+ getTestParameters()
+ .withDexRuntimes()
+ .withApiLevelsStartingAtIncluding(AndroidApiLevel.L)
+ .build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ assertTrue(parameters.canHaveNonReboundConstructorInvoke());
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepRules("-repackageclasses")
+ .addOptionsModification(
+ options ->
+ options
+ .getRedundantBridgeRemovalOptions()
+ .setEnableRetargetingOfConstructorBridgeCalls(
+ enableRetargetingOfConstructorBridgeCalls))
+ .addOptionsModification(
+ options -> options.horizontalClassMergerOptions().disableInitialRoundOfClassMerging())
+ .addHorizontallyMergedClassesInspector(
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ .enableConstantArgumentAnnotations()
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableUnusedArgumentAnnotations()
+ .setMinApi(parameters)
+ .compile()
+ .inspect(
+ inspector -> {
+ // Verify Parent and Parent.<init>(Parent) are present and that Parent has been
+ // repackaged
+ // into the default package.
+ ClassSubject parentClassSubject = inspector.clazz(Parent.class);
+ assertThat(parentClassSubject, isPresent());
+ assertEquals("", parentClassSubject.getDexProgramClass().getType().getPackageName());
+
+ MethodSubject parentInstanceInitializerSubject =
+ parentClassSubject.uniqueInstanceInitializer();
+ assertThat(parentInstanceInitializerSubject, isPresent());
+ assertEquals(
+ parentClassSubject.asTypeSubject(),
+ parentInstanceInitializerSubject.getParameter(0));
+
+ // Verify that A and A.<init>(Parent) are present.
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ assertEquals("", aClassSubject.getDexProgramClass().getType().getPackageName());
+
+ MethodSubject aInstanceInitializerSubject = aClassSubject.uniqueInstanceInitializer();
+ assertThat(aInstanceInitializerSubject, isPresent());
+ assertEquals(
+ parentClassSubject.asTypeSubject(), aInstanceInitializerSubject.getParameter(0));
+
+ // Verify that B's initializer was removed.
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+ assertThat(bClassSubject, isPresent());
+ assertEquals("", bClassSubject.getDexProgramClass().getType().getPackageName());
+ assertEquals(
+ 0, bClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("B");
+ }
+
+ public static class Main {
+
+ static {
+ new B(null).setFieldOnB().printFieldOnB();
+ }
+
+ public static void main(String[] args) {
+ if (System.currentTimeMillis() < 0) {
+ new A(null).setFieldOnA().printFieldOnA();
+ }
+ }
+ }
+
+ public static class Parent {
+
+ @KeepConstantArguments
+ @KeepUnusedArguments
+ public Parent(Parent parent) {}
+ }
+
+ @NeverClassInline
+ public static class A extends Parent {
+
+ Object field;
+
+ @KeepConstantArguments
+ public A(Parent parent) {
+ super(parent);
+ System.out.println("Ouch!");
+ }
+
+ @NeverInline
+ public A setFieldOnA() {
+ field = "A";
+ return this;
+ }
+
+ @NeverInline
+ public void printFieldOnA() {
+ System.out.println(field);
+ }
+ }
+
+ @NeverClassInline
+ public static class B extends Parent {
+
+ Object field;
+
+ // Removed by constructor shrinking.
+ @KeepConstantArguments
+ public B(Parent parent) {
+ super(parent);
+ }
+
+ @NeverInline
+ public B setFieldOnB() {
+ field = "B";
+ return this;
+ }
+
+ @NeverInline
+ public void printFieldOnB() {
+ System.out.println(field);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
index 2205c9c..14aff54 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
@@ -5,15 +5,16 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command;
import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.List;
import org.apache.harmony.jpda.tests.framework.jdwp.Value;
import org.junit.Test;
@@ -257,11 +258,6 @@
@Test
public void testNestedInlining() throws Throwable {
- assumeTrue(
- "b/244704042: Incorrect step-into StringBuilder.",
- parameters.isCfRuntime()
- || !(parameters.getDexRuntimeVersion().isEqualTo(Version.V13_0_0)
- || parameters.getDexRuntimeVersion().isEqualTo(Version.V14_0_0)));
// Count the number of lines in the source file. This is needed to check that inlined code
// refers to non-existing line numbers.
Path sourceFilePath =
@@ -343,13 +339,7 @@
stepInto(),
checkLocals(left_mangledLvName, right_mangledLvName),
// Enter "foo"
- stepInto(),
- // See b/207743106 for incorrect debug info on Kotlin 1.6.
- applyIf(
- kotlinParameters.getCompilerVersion() == KotlinCompilerVersion.KOTLINC_1_6_0,
- this::stepInto),
- checkMethod(DEBUGGEE_CLASS, "foo"),
- checkLine(SOURCE_FILE, 34),
+ stepIntoFooWithWorkaroundKotlin16(),
stepOut(),
// We're back to the inline section, at the end of the lambda
inspect(
@@ -401,15 +391,27 @@
checkLocal(inlinee2_inlineScope),
checkNoLocal(inlinee2_lambda1_inlineScope),
checkNoLocal(inlinee2_lambda2_inlineScope),
- // Enter the call to "foo"
- stepInto(),
- // See b/207743106 for incorrect debug info on Kotlin 1.6.
- applyIf(
- kotlinParameters.getCompilerVersion() == KotlinCompilerVersion.KOTLINC_1_6_0,
- this::stepInto),
- checkMethod(DEBUGGEE_CLASS, "foo"),
- checkLine(SOURCE_FILE, 34),
+ stepIntoFooWithWorkaroundKotlin16(),
run());
}
+ // See b/207743106 for incorrect debug info on Kotlin 1.6.
+ private Command stepIntoFooWithWorkaroundKotlin16() {
+ boolean is16 = kotlinParameters.getCompilerVersion() == KotlinCompilerVersion.KOTLINC_1_6_0;
+ boolean stepHitsStringBuilder = parameters.isDexRuntimeVersionNewerThanOrEqual(Version.V13_0_0);
+ List<Command> commands = new ArrayList<>();
+ // Enter the call to "foo"
+ commands.add(stepInto());
+ // The code on kotlin 1.6 does not have an active line on entry, so advance to hit it.
+ if (is16) {
+ commands.add(stepInto());
+ // On newer VMs the stepInput will enter StringBuilder, so step out of that again.
+ if (stepHitsStringBuilder) {
+ commands.add(stepOut());
+ }
+ }
+ commands.add(checkMethod(DEBUGGEE_CLASS, "foo"));
+ commands.add(checkLine(SOURCE_FILE, 34));
+ return subcommands(commands);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
index 1ea71b6..b935de4 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestBuilder;
+import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -144,7 +145,7 @@
ignoredInvokes.add(methodName);
}
- private void configureProgram(TestBuilder<?, ?> builder) throws IOException {
+ protected void configureProgram(TestBuilder<?, ?> builder) throws IOException {
builder.addProgramClasses(MiniAssert.class, IgnoreInvokes.class);
if (testClass != null) {
testClass.addAsProgramClass(builder);
@@ -162,33 +163,45 @@
.assertSuccess();
}
+ private void checkDiagnostics(TestDiagnosticMessages diagnostics) {
+ if (diagnostics.getWarnings().isEmpty()) {
+ diagnostics.assertNoMessages();
+ return;
+ }
+ // When compiling with an old android.jar some tests refer to non-present types.
+ // Check only java.util types are missing and that none of them are about the target
+ // type that is being backported.
+ diagnostics
+ .assertOnlyWarnings()
+ .assertAllWarningsMatch(diagnosticType(InterfaceDesugarMissingTypeDiagnostic.class))
+ .assertAllWarningsMatch(diagnosticMessage(containsString("java.util")))
+ .assertNoWarningsMatch(diagnosticMessage(containsString(targetClass.getName())));
+ }
+
@Test
public void testD8() throws Exception {
parameters.assumeDexRuntime();
- testForD8()
- .setMinApi(parameters)
- .apply(this::configureProgram)
- .setIncludeClassesChecksum(true)
- .compileWithExpectedDiagnostics(
- diagnostics -> {
- if (diagnostics.getWarnings().isEmpty()) {
- diagnostics.assertNoMessages();
- return;
- }
- // When compiling with an old android.jar some tests refer to non-present types.
- // Check only java.util types are missing and that none of them are about the target
- // type that is being backported.
- diagnostics
- .assertOnlyWarnings()
- .assertAllWarningsMatch(
- diagnosticType(InterfaceDesugarMissingTypeDiagnostic.class))
- .assertAllWarningsMatch(diagnosticMessage(containsString("java.util")))
- .assertNoWarningsMatch(
- diagnosticMessage(containsString(targetClass.getName())));
- })
- .run(parameters.getRuntime(), testClassName)
- .assertSuccess()
- .inspect(this::assertDesugaring);
+ testForD8()
+ .setMinApi(parameters)
+ .apply(this::configureProgram)
+ .setIncludeClassesChecksum(true)
+ .compileWithExpectedDiagnostics(this::checkDiagnostics)
+ .run(parameters.getRuntime(), testClassName)
+ .assertSuccess()
+ .inspect(this::assertDesugaring);
+ }
+
+ @Test
+ public void testD8Cf() throws Exception {
+ parameters.assumeCfRuntime();
+ testForD8(Backend.CF)
+ .setMinApi(parameters)
+ .apply(this::configureProgram)
+ .setIncludeClassesChecksum(true)
+ .compileWithExpectedDiagnostics(this::checkDiagnostics)
+ .run(parameters.getRuntime(), testClassName)
+ .assertSuccess()
+ .inspect(this::assertDesugaring);
}
private void assertDesugaring(CodeInspector inspector) {
@@ -219,6 +232,10 @@
+ javaInvokeStatics, expectedTargetInvokes, actualTargetInvokes);
}
+ public String getTestClassName() {
+ return testClassName;
+ }
+
/** JUnit {@link Assert} isn't available in the VM runtime. This is a mini mirror of its API. */
static abstract class MiniAssert {
static void assertTrue(boolean value) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceArrayTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceArrayTest.java
index 1c95fa0..b4ec061 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceArrayTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceArrayTest.java
@@ -19,7 +19,8 @@
public static Iterable<?> data() {
return getTestParameters()
.withDexRuntimesStartingFromExcluding(Version.V4_0_4)
- .withAllApiLevels()
+ .withCfRuntimes()
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceFieldUpdaterTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceFieldUpdaterTest.java
index ac1bb54..50e0d50 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceFieldUpdaterTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceFieldUpdaterTest.java
@@ -19,7 +19,8 @@
public static Iterable<?> data() {
return getTestParameters()
.withDexRuntimesStartingFromExcluding(Version.V4_0_4)
- .withAllApiLevels()
+ .withCfRuntimes()
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceTest.java
index 8506aec..64a27bf 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceTest.java
@@ -19,7 +19,8 @@
public static Iterable<?> data() {
return getTestParameters()
.withDexRuntimesStartingFromExcluding(Version.V4_0_4)
- .withAllApiLevels()
+ .withCfRuntimes()
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BooleanBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BooleanBackportTest.java
index 3e4b973..ccee141 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/BooleanBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/BooleanBackportTest.java
@@ -14,9 +14,7 @@
public final class BooleanBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters()
- .withAllRuntimesAndApiLevels()
- .build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public BooleanBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportJava9Test.java
index 81038d6..010f36d 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportJava9Test.java
@@ -23,7 +23,7 @@
return getTestParameters()
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
.withDexRuntimes()
- .withAllApiLevels()
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportTest.java
index e92fcc7..eccbd85 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ByteBackportTest.java
@@ -14,9 +14,7 @@
public final class ByteBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters()
- .withAllRuntimesAndApiLevels()
- .build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public ByteBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/CharSequenceBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/CharSequenceBackportJava11Test.java
index 65f2f55..42a85c0 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/CharSequenceBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/CharSequenceBackportJava11Test.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.desugar.backports;
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper;
@@ -13,16 +15,14 @@
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
-import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-
@RunWith(Parameterized.class)
public final class CharSequenceBackportJava11Test extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
return getTestParameters()
.withDexRuntimes()
- .withAllApiLevels()
.withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportJava11Test.java
index 9a03c16..735ba69 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportJava11Test.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.desugar.backports;
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper;
@@ -13,16 +15,14 @@
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
-import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-
@RunWith(Parameterized.class)
public final class CharacterBackportJava11Test extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
return getTestParameters()
.withDexRuntimes()
- .withAllApiLevels()
.withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportTest.java
index e4eef82..2345798 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/CharacterBackportTest.java
@@ -14,9 +14,7 @@
public final class CharacterBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters()
- .withAllRuntimesAndApiLevels()
- .build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public CharacterBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/CollectionsBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/CollectionsBackportTest.java
index 66af3d0..078bff1 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/CollectionsBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/CollectionsBackportTest.java
@@ -19,9 +19,7 @@
public final class CollectionsBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters()
- .withAllRuntimesAndApiLevels()
- .build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public CollectionsBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ContentProviderClientBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ContentProviderClientBackportTest.java
index e15b419..8d3b5f2 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ContentProviderClientBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ContentProviderClientBackportTest.java
@@ -4,15 +4,15 @@
package com.android.tools.r8.desugar.backports;
-import static org.junit.Assert.assertTrue;
+import static org.hamcrest.CoreMatchers.containsString;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
+import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@@ -22,7 +22,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withDexRuntimesAndAllApiLevels().build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public ContentProviderClientBackportTest(TestParameters parameters) throws IOException {
@@ -42,9 +42,18 @@
registerTarget(AndroidApiLevel.N, 1);
}
+ @Test
+ public void testJvm() throws Exception {
+ parameters.assumeJvmTestParameters();
+ testForJvm(parameters)
+ .apply(this::configureProgram)
+ .run(parameters.getRuntime(), getTestClassName())
+ // Fails when not desugared.
+ .assertFailureWithErrorThatMatches(containsString("Failed: close should not be called"));
+ }
+
private static byte[] getContentProviderClient(TestParameters parameters) throws IOException {
if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)) {
- assertTrue(parameters.getRuntime().asDex().getVm().isNewerThanOrEqual(DexVm.ART_7_0_0_HOST));
return transformer(ContentProviderClientApiLevel24.class)
.setClassDescriptor(DexItemFactory.androidContentContentProviderClientDescriptorString)
.transform();
@@ -70,8 +79,9 @@
TestRunner.doFail("close should not be called");
}
- public void release() {
+ public boolean release() {
wasClosed = true;
+ return wasClosed;
}
}
@@ -82,8 +92,9 @@
wasClosed = true;
}
- public void release() {
+ public boolean release() {
TestRunner.doFail("release should not be called");
+ return true;
}
}
@@ -92,8 +103,11 @@
public static void main(String[] args) {
ContentProviderClient contentProviderClient = new ContentProviderClient();
MiniAssert.assertFalse(contentProviderClient.wasClosed);
- contentProviderClient.close();
- MiniAssert.assertTrue(contentProviderClient.wasClosed);
+ // Loop as regression test for b/276874854.
+ for (int i = 0; i < 2; i++) {
+ contentProviderClient.close();
+ MiniAssert.assertTrue(contentProviderClient.wasClosed);
+ }
}
// Forwards to MiniAssert to avoid having to make it public.
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/DoubleBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/DoubleBackportTest.java
index 16cdeda..ecd8ea9 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/DoubleBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/DoubleBackportTest.java
@@ -14,9 +14,7 @@
public final class DoubleBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters()
- .withAllRuntimesAndApiLevels()
- .build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public DoubleBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/DrmManagerClientBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/DrmManagerClientBackportTest.java
index 6b16c6c..5d3ee55 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/DrmManagerClientBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/DrmManagerClientBackportTest.java
@@ -4,15 +4,15 @@
package com.android.tools.r8.desugar.backports;
-import static org.junit.Assert.assertTrue;
+import static org.hamcrest.CoreMatchers.containsString;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
+import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@@ -22,7 +22,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withDexRuntimesAndAllApiLevels().build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public DrmManagerClientBackportTest(TestParameters parameters) throws IOException {
@@ -42,9 +42,18 @@
registerTarget(AndroidApiLevel.N, 1);
}
+ @Test
+ public void testJvm() throws Exception {
+ parameters.assumeJvmTestParameters();
+ testForJvm(parameters)
+ .apply(this::configureProgram)
+ .run(parameters.getRuntime(), getTestClassName())
+ // Fails when not desugared.
+ .assertFailureWithErrorThatMatches(containsString("Failed: close should not be called"));
+ }
+
private static byte[] getDrmManagerClient(TestParameters parameters) throws IOException {
if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)) {
- assertTrue(parameters.getRuntime().asDex().getVm().isNewerThanOrEqual(DexVm.ART_7_0_0_HOST));
return transformer(DrmManagerClientApiLevel24.class)
.setClassDescriptor(DexItemFactory.androidDrmDrmManagerClientDescriptorString)
.transform();
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/FloatBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/FloatBackportTest.java
index 92db3a6..591a41e 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/FloatBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/FloatBackportTest.java
@@ -14,9 +14,7 @@
public final class FloatBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters()
- .withAllRuntimesAndApiLevels()
- .build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public FloatBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportJava9Test.java
index f8c5a53..5cc2e23 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportJava9Test.java
@@ -23,7 +23,7 @@
return getTestParameters()
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
.withDexRuntimes()
- .withAllApiLevels()
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportTest.java
index ed697a7..0d5ac49 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportTest.java
@@ -15,9 +15,7 @@
public final class IntegerBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters()
- .withAllRuntimesAndApiLevels()
- .build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public IntegerBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava10Test.java
index 7b80e96..0f63b03 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava10Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava10Test.java
@@ -24,7 +24,7 @@
return getTestParameters()
.withCfRuntimesStartingFromIncluding(CfVm.JDK10)
.withDexRuntimes()
- .withAllApiLevels()
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava9Test.java
index ddf502b..604e82c 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ListBackportJava9Test.java
@@ -26,7 +26,7 @@
return getTestParameters()
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
.withDexRuntimes()
- .withAllApiLevels()
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportJava9Test.java
index 487f80f..8ffb905 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportJava9Test.java
@@ -23,7 +23,7 @@
return getTestParameters()
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
.withDexRuntimes()
- .withAllApiLevels()
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportSingleMethodTest.java b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportSingleMethodTest.java
index 7344bd8..39cc434 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportSingleMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportSingleMethodTest.java
@@ -20,9 +20,7 @@
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters()
- .withAllRuntimesAndApiLevels()
- .build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public LongBackportSingleMethodTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportTest.java
index f8a6ad8..cbcf278 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportTest.java
@@ -15,9 +15,7 @@
public final class LongBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters()
- .withAllRuntimesAndApiLevels()
- .build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public LongBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava10Test.java
index 11008cc..19c89e5 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava10Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava10Test.java
@@ -24,7 +24,7 @@
return getTestParameters()
.withCfRuntimesStartingFromIncluding(CfVm.JDK10)
.withDexRuntimes()
- .withAllApiLevels()
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava9Test.java
index 73ffa29..a5c1b1f 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MapBackportJava9Test.java
@@ -26,7 +26,7 @@
return getTestParameters()
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
.withDexRuntimes()
- .withAllApiLevels()
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava17Test.java b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava17Test.java
index 8f9332d..c612792 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava17Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava17Test.java
@@ -22,8 +22,8 @@
public static Iterable<?> data() {
return getTestParameters()
.withDexRuntimes()
- .withAllApiLevels()
.withCfRuntimesStartingFromIncluding(CfVm.JDK17)
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava9Test.java
index 2f70fc2..f41ce56 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportJava9Test.java
@@ -22,8 +22,8 @@
public static Iterable<?> data() {
return getTestParameters()
.withDexRuntimes()
- .withAllApiLevels()
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportTest.java
index acae4c9..36aa95e 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MathBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MathBackportTest.java
@@ -14,9 +14,7 @@
public final class MathBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters()
- .withAllRuntimesAndApiLevels()
- .build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public MathBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MediaDrmBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/MediaDrmBackportTest.java
index 500308b..144d841 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MediaDrmBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MediaDrmBackportTest.java
@@ -4,15 +4,15 @@
package com.android.tools.r8.desugar.backports;
-import static org.junit.Assert.assertTrue;
+import static org.hamcrest.CoreMatchers.containsString;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
+import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@@ -22,7 +22,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withDexRuntimesAndAllApiLevels().build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public MediaDrmBackportTest(TestParameters parameters) throws IOException {
@@ -41,9 +41,18 @@
registerTarget(AndroidApiLevel.P, 1);
}
+ @Test
+ public void testJvm() throws Exception {
+ parameters.assumeJvmTestParameters();
+ testForJvm(parameters)
+ .apply(this::configureProgram)
+ .run(parameters.getRuntime(), getTestClassName())
+ // Fails when not desugared.
+ .assertFailureWithErrorThatMatches(containsString("Failed: close should not be called"));
+ }
+
private static byte[] getMediaDrm(TestParameters parameters) throws IOException {
if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.P)) {
- assertTrue(parameters.getRuntime().asDex().getVm().isNewerThanOrEqual(DexVm.ART_8_1_0_HOST));
return transformer(MediaDrmApiLevel28.class)
.setClassDescriptor(DexItemFactory.androidMediaMediaDrmDescriptorString)
.transform();
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MediaMetadataRetrieverBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/MediaMetadataRetrieverBackportTest.java
index b3cbfe2..d2cb8f8 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MediaMetadataRetrieverBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MediaMetadataRetrieverBackportTest.java
@@ -4,15 +4,15 @@
package com.android.tools.r8.desugar.backports;
-import static org.junit.Assert.assertTrue;
+import static org.hamcrest.CoreMatchers.containsString;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
+import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@@ -22,7 +22,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withDexRuntimesAndAllApiLevels().build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public MediaMetadataRetrieverBackportTest(TestParameters parameters) throws IOException {
@@ -42,9 +42,18 @@
registerTarget(AndroidApiLevel.Q, 1);
}
+ @Test
+ public void testJvm() throws Exception {
+ parameters.assumeJvmTestParameters();
+ testForJvm(parameters)
+ .apply(this::configureProgram)
+ .run(parameters.getRuntime(), getTestClassName())
+ // Fails when not desugared.
+ .assertFailureWithErrorThatMatches(containsString("Failed: close should not be called"));
+ }
+
private static byte[] getMediaMetadataRetriever(TestParameters parameters) throws IOException {
if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.Q)) {
- assertTrue(parameters.getRuntime().asDex().getVm().isNewerThanOrEqual(DexVm.ART_10_0_0_HOST));
return transformer(MediaMetadataRetrieverApiLevel29.class)
.setClassDescriptor(DexItemFactory.androidMediaMediaMetadataRetrieverDescriptorString)
.transform();
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/MethodBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/MethodBackportTest.java
index 78c2770..3597845 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/MethodBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/MethodBackportTest.java
@@ -14,7 +14,7 @@
public final class MethodBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public MethodBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava17Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava17Test.java
index 4a5345fe..af9af9d 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava17Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava17Test.java
@@ -23,8 +23,8 @@
public static Iterable<?> data() {
return getTestParameters()
.withDexRuntimes()
- .withAllApiLevels()
.withCfRuntimesStartingFromIncluding(CfVm.JDK17)
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java
index f926a22..3864c5e 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportJava9Test.java
@@ -25,8 +25,8 @@
public static Iterable<?> data() {
return getTestParameters()
.withDexRuntimes()
- .withAllApiLevels()
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportTest.java
index 5e034bc..63a9e06 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ObjectsBackportTest.java
@@ -17,9 +17,7 @@
public final class ObjectsBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters()
- .withAllRuntimesAndApiLevels()
- .build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public ObjectsBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava10Test.java
index c5e6adc..956a2fb 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava10Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava10Test.java
@@ -26,6 +26,7 @@
.withDexRuntimesStartingFromIncluding(Version.V7_0_0)
.withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
.withCfRuntimesStartingFromIncluding(CfVm.JDK10)
+ .enableApiLevelsForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava11Test.java
index 8b57c0f..a7255b6 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava11Test.java
@@ -26,6 +26,7 @@
.withDexRuntimesStartingFromIncluding(Version.V7_0_0)
.withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
.withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+ .enableApiLevelsForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java
index a726dc5..e71d3ca 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalBackportJava9Test.java
@@ -26,6 +26,7 @@
.withDexRuntimesStartingFromIncluding(Version.V7_0_0)
.withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+ .enableApiLevelsForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava10Test.java
index a181a64..794e049 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava10Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava10Test.java
@@ -26,6 +26,7 @@
.withDexRuntimesStartingFromIncluding(Version.V7_0_0)
.withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
.withCfRuntimesStartingFromIncluding(CfVm.JDK10)
+ .enableApiLevelsForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava11Test.java
index b5e9c02..377ed2e 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava11Test.java
@@ -26,6 +26,7 @@
.withDexRuntimesStartingFromIncluding(Version.V7_0_0)
.withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
.withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+ .enableApiLevelsForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava9Test.java
index bb9bb65..b3dacdd 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalDoubleBackportJava9Test.java
@@ -26,6 +26,7 @@
.withDexRuntimesStartingFromIncluding(Version.V7_0_0)
.withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+ .enableApiLevelsForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava10Test.java
index 00f5b4b..ab44630 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava10Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava10Test.java
@@ -26,6 +26,7 @@
.withDexRuntimesStartingFromIncluding(Version.V7_0_0)
.withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
.withCfRuntimesStartingFromIncluding(CfVm.JDK10)
+ .enableApiLevelsForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava11Test.java
index 62d66cd..95c6adc 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava11Test.java
@@ -26,6 +26,7 @@
.withDexRuntimesStartingFromIncluding(Version.V7_0_0)
.withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
.withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+ .enableApiLevelsForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava9Test.java
index 54f08ae..d061489 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalIntBackportJava9Test.java
@@ -26,6 +26,7 @@
.withDexRuntimesStartingFromIncluding(Version.V7_0_0)
.withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+ .enableApiLevelsForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava10Test.java
index c353e92..5621a3c 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava10Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava10Test.java
@@ -26,6 +26,7 @@
.withDexRuntimesStartingFromIncluding(Version.V7_0_0)
.withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
.withCfRuntimesStartingFromIncluding(CfVm.JDK10)
+ .enableApiLevelsForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava11Test.java
index fd8fc6f..7333cb9 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava11Test.java
@@ -26,6 +26,7 @@
.withDexRuntimesStartingFromIncluding(Version.V7_0_0)
.withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
.withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+ .enableApiLevelsForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava9Test.java
index efb516a..ea02d61 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/OptionalLongBackportJava9Test.java
@@ -26,6 +26,7 @@
.withDexRuntimesStartingFromIncluding(Version.V7_0_0)
.withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+ .enableApiLevelsForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/PredicateBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/PredicateBackportJava11Test.java
index f98f769..d0a6c35 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/PredicateBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/PredicateBackportJava11Test.java
@@ -26,6 +26,7 @@
.withDexRuntimesStartingFromIncluding(Version.V7_0_0)
.withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
.withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+ .enableApiLevelsForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava10Test.java b/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava10Test.java
index 2a17cb2..ec22ac6 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava10Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava10Test.java
@@ -24,7 +24,7 @@
return getTestParameters()
.withCfRuntimesStartingFromIncluding(CfVm.JDK10)
.withDexRuntimes()
- .withAllApiLevels()
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava9Test.java
index 652cde0..efdc935 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/SetBackportJava9Test.java
@@ -26,7 +26,7 @@
return getTestParameters()
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
.withDexRuntimes()
- .withAllApiLevels()
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportJava9Test.java
index d1d3c95..d641452 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportJava9Test.java
@@ -23,7 +23,7 @@
return getTestParameters()
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
.withDexRuntimes()
- .withAllApiLevels()
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportTest.java
index 2d1a61a..05bc2db 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ShortBackportTest.java
@@ -14,9 +14,7 @@
public final class ShortBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters()
- .withAllRuntimesAndApiLevels()
- .build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public ShortBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/SparseArrayBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/SparseArrayBackportTest.java
index 1ebfe97..de8a8d1 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/SparseArrayBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/SparseArrayBackportTest.java
@@ -4,11 +4,13 @@
package com.android.tools.r8.desugar.backports;
+import static org.hamcrest.CoreMatchers.containsString;
+
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
+import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@@ -20,7 +22,7 @@
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public SparseArrayBackportTest(TestParameters parameters) throws IOException {
@@ -40,9 +42,18 @@
registerTarget(AndroidApiLevel.S, 1);
}
+ @Test
+ public void testJvm() throws Exception {
+ parameters.assumeJvmTestParameters();
+ testForJvm(parameters)
+ .apply(this::configureProgram)
+ .run(parameters.getRuntime(), getTestClassName())
+ // Fails when not desugared.
+ .assertFailureWithErrorThatMatches(containsString("Failed: set should not be called"));
+ }
+
private static byte[] getSparseArray(TestParameters parameters) throws IOException {
if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.S)) {
- assert parameters.getRuntime().asDex().getVm().isNewerThanOrEqual(DexVm.ART_12_0_0_HOST);
return transformer(SparseArrayAndroid12.class)
.setClassDescriptor(SPARSE_ARRAY_DESCRIPTOR)
.transform();
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StreamBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/StreamBackportJava9Test.java
index 5e33276..2416783 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/StreamBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StreamBackportJava9Test.java
@@ -26,6 +26,7 @@
.withDexRuntimesStartingFromIncluding(Version.V7_0_0)
.withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+ .enableApiLevelsForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava17Test.java b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava17Test.java
index c93b1ee..f180eab 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava17Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava17Test.java
@@ -23,8 +23,8 @@
public static Iterable<?> data() {
return getTestParameters()
.withDexRuntimes()
- .withAllApiLevels()
.withCfRuntimesStartingFromIncluding(CfVm.JDK17)
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava9Test.java
index 2f124e7..105c190 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportJava9Test.java
@@ -21,8 +21,8 @@
public static Iterable<?> data() {
return getTestParameters()
.withDexRuntimes()
- .withAllApiLevels()
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportTest.java
index 1926975..b93bd2f 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StrictMathBackportTest.java
@@ -14,9 +14,7 @@
public final class StrictMathBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters()
- .withAllRuntimesAndApiLevels()
- .build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public StrictMathBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StringBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/StringBackportJava11Test.java
index 6290238..e6d81ac 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/StringBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StringBackportJava11Test.java
@@ -22,8 +22,8 @@
public static Iterable<?> data() {
return getTestParameters()
.withDexRuntimes()
- .withAllApiLevels()
.withCfRuntimesStartingFromIncluding(CfVm.JDK11)
+ .withAllApiLevelsAlsoForCf()
.build();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StringBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/StringBackportTest.java
index ff4350f..37d8462 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/StringBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StringBackportTest.java
@@ -4,22 +4,20 @@
package com.android.tools.r8.desugar.backports;
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.AndroidApiLevel;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
-import static java.util.Arrays.asList;
-import static java.util.Collections.emptyList;
-
@RunWith(Parameterized.class)
public final class StringBackportTest extends AbstractBackportTest {
@Parameters(name = "{0}")
public static Iterable<?> data() {
- return getTestParameters()
- .withAllRuntimesAndApiLevels()
- .build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public StringBackportTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/TypedArrayBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/TypedArrayBackportTest.java
index 06e759d..1b7e834 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/TypedArrayBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/TypedArrayBackportTest.java
@@ -4,15 +4,15 @@
package com.android.tools.r8.desugar.backports;
-import static org.junit.Assert.assertTrue;
+import static org.hamcrest.CoreMatchers.containsString;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
+import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@@ -22,7 +22,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withDexRuntimesAndAllApiLevels().build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public TypedArrayBackportTest(TestParameters parameters) throws IOException {
@@ -42,9 +42,18 @@
registerTarget(AndroidApiLevel.S, 1);
}
+ @Test
+ public void testJvm() throws Exception {
+ parameters.assumeJvmTestParameters();
+ testForJvm(parameters)
+ .apply(this::configureProgram)
+ .run(parameters.getRuntime(), getTestClassName())
+ // Fails when not desugared.
+ .assertFailureWithErrorThatMatches(containsString("Failed: close should not be called"));
+ }
+
private static byte[] getTypedArray(TestParameters parameters) throws IOException {
if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.S)) {
- assertTrue(parameters.getRuntime().asDex().getVm().isNewerThanOrEqual(DexVm.ART_12_0_0_HOST));
return transformer(TypedArrayAndroidApiLevel31.class)
.setClassDescriptor(DexItemFactory.androidContentResTypedArrayDescriptorString)
.transform();
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
index bb382f8..2cdeffd 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
@@ -26,7 +26,6 @@
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
-import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
import com.android.tools.r8.ir.conversion.MethodProcessorWithWave;
@@ -97,11 +96,7 @@
method -> {
IRCode code = method.buildIR(appView);
fieldAccessAnalysis.recordFieldAccesses(
- code,
- BytecodeMetadataProvider.builder(),
- feedback,
- FieldReadBeforeWriteAnalysis.trivial(),
- new PrimaryMethodProcessorMock());
+ code, BytecodeMetadataProvider.builder(), feedback, new PrimaryMethodProcessorMock());
});
int bitsReadInBitField = feedback.bitsReadPerField.getInt(uniqueFieldByName(clazz, "bitField"));
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonNullInstanceFieldPropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonNullInstanceFieldPropagationTest.java
deleted file mode 100644
index 972cc79..0000000
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonNullInstanceFieldPropagationTest.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// 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.ir.optimize.membervaluepropagation;
-
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-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 NonNullInstanceFieldPropagationTest extends TestBase {
-
- @Parameter(0)
- public TestParameters parameters;
-
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
- }
-
- @Test
- public void test() throws Exception {
- testForR8(parameters.getBackend())
- .addInnerClasses(getClass())
- .addKeepMainRule(Main.class)
- .setMinApi(parameters)
- .compile()
- .inspect(
- inspector -> {
- ClassSubject mainClassSubject = inspector.clazz(Main.class);
- assertThat(mainClassSubject, isPresent());
- assertEquals(1, inspector.allClasses().size());
- assertEquals(1, mainClassSubject.allMethods().size());
- assertEquals(0, mainClassSubject.allFields().size());
- })
- .run(parameters.getRuntime(), Main.class)
- .assertSuccessWithEmptyOutput();
- }
-
- static class Main {
-
- public static void main(String[] args) {
- Main p = new Main();
- p.mThreadChecker.assertOnValidThread();
- }
-
- public final ThreadChecker mThreadChecker = new ThreadChecker();
- }
-
- static class ThreadChecker {
- public void assertOnValidThread() {
- if (System.currentTimeMillis() == 0) {
- throw new RuntimeException();
- }
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
index 8078ed6..92404f0 100644
--- a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
@@ -101,7 +101,6 @@
" void <init>();",
"}",
"-neverclassinline class *",
- "-nohorizontalclassmerging class adaptresourcefilenames.B",
"-nohorizontalclassmerging class adaptresourcefilenames.pkg.C",
"-nohorizontalclassmerging class adaptresourcefilenames.pkg.innerpkg.D");
}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
index 2d38860..c83d81d 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
@@ -75,15 +75,22 @@
}
private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector) {
- inspect(profileInspector, inspector, false);
+ inspect(profileInspector, inspector, false, false);
}
private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector) {
- inspect(profileInspector, inspector, parameters.isCfRuntime());
+ inspect(
+ profileInspector,
+ inspector,
+ parameters.canHaveNonReboundConstructorInvoke(),
+ parameters.isCfRuntime());
}
public void inspect(
- ArtProfileInspector profileInspector, CodeInspector inspector, boolean canUseLambdas) {
+ ArtProfileInspector profileInspector,
+ CodeInspector inspector,
+ boolean canHaveNonReboundConstructorInvoke,
+ boolean canUseLambdas) {
ClassSubject mainClassSubject = inspector.clazz(Main.class);
assertThat(mainClassSubject, isPresent());
@@ -96,21 +103,26 @@
assertThat(lambdaClassSubject, notIf(isPresent(), canUseLambdas));
MethodSubject lambdaInitializerSubject = lambdaClassSubject.uniqueInstanceInitializer();
- assertThat(lambdaInitializerSubject, notIf(isPresent(), canUseLambdas));
+ assertThat(
+ lambdaInitializerSubject,
+ notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseLambdas));
MethodSubject lambdaMainMethodSubject =
lambdaClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
assertThat(lambdaMainMethodSubject, notIf(isPresent(), canUseLambdas));
if (canUseLambdas) {
- profileInspector.assertContainsMethodRule(mainMethodSubject).assertContainsNoOtherRules();
+ profileInspector.assertContainsMethodRule(mainMethodSubject);
} else {
profileInspector
.assertContainsClassRules(lambdaClassSubject)
- .assertContainsMethodRules(
- mainMethodSubject, lambdaInitializerSubject, lambdaMainMethodSubject)
- .assertContainsNoOtherRules();
+ .assertContainsMethodRules(mainMethodSubject, lambdaMainMethodSubject)
+ .applyIf(
+ !canHaveNonReboundConstructorInvoke,
+ i -> i.assertContainsMethodRule(lambdaInitializerSubject));
}
+
+ profileInspector.assertContainsNoOtherRules();
}
static class Main {
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
index 7d9e9ab..577518d 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
@@ -104,17 +104,22 @@
private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector)
throws Exception {
- inspect(profileInspector, inspector, parameters.canUseNestBasedAccessesWhenDesugaring());
+ inspect(profileInspector, inspector, false, parameters.canUseNestBasedAccessesWhenDesugaring());
}
private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector)
throws Exception {
- inspect(profileInspector, inspector, parameters.canUseNestBasedAccesses());
+ inspect(
+ profileInspector,
+ inspector,
+ parameters.canHaveNonReboundConstructorInvoke(),
+ parameters.canUseNestBasedAccesses());
}
private void inspect(
ArtProfileInspector profileInspector,
CodeInspector inspector,
+ boolean canHaveNonReboundConstructorInvoke,
boolean canUseNestBasedAccesses)
throws Exception {
ClassSubject nestMemberClassSubject = inspector.clazz(NestMember.class);
@@ -128,7 +133,7 @@
syntheticConstructorArgumentClassSubject, notIf(isPresent(), canUseNestBasedAccesses));
MethodSubject instanceInitializer = nestMemberClassSubject.init();
- assertThat(instanceInitializer, isPresent());
+ assertThat(instanceInitializer, notIf(isPresent(), canHaveNonReboundConstructorInvoke));
MethodSubject instanceInitializerWithSyntheticArgumentSubject =
syntheticConstructorArgumentClassSubject.isPresent()
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
index 5f1886d..6d03704 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
@@ -149,6 +149,7 @@
profileInspector,
inspector,
SyntheticItemsTestUtils.syntheticRecordTagClass(),
+ false,
parameters.canUseNestBasedAccessesWhenDesugaring(),
parameters.canUseRecordsWhenDesugaring());
}
@@ -158,6 +159,7 @@
profileInspector,
inspector,
RECORD_REFERENCE,
+ parameters.canHaveNonReboundConstructorInvoke(),
parameters.canUseNestBasedAccesses(),
parameters.canUseRecords());
}
@@ -166,6 +168,7 @@
ArtProfileInspector profileInspector,
CodeInspector inspector,
ClassReference recordClassReference,
+ boolean canHaveNonReboundConstructorInvoke,
boolean canUseNestBasedAccesses,
boolean canUseRecords) {
ClassSubject mainClassSubject = inspector.clazz(MAIN_REFERENCE);
@@ -177,11 +180,14 @@
ClassSubject recordTagClassSubject = inspector.clazz(recordClassReference);
assertThat(recordTagClassSubject, notIf(isPresent(), canUseRecords));
if (!canUseRecords) {
- assertEquals(1, recordTagClassSubject.allMethods().size());
+ assertEquals(
+ canHaveNonReboundConstructorInvoke ? 0 : 1, recordTagClassSubject.allMethods().size());
}
MethodSubject recordTagInstanceInitializerSubject = recordTagClassSubject.init();
- assertThat(recordTagInstanceInitializerSubject, notIf(isPresent(), canUseRecords));
+ assertThat(
+ recordTagInstanceInitializerSubject,
+ notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseRecords));
ClassSubject personRecordClassSubject = inspector.clazz(PERSON_REFERENCE);
assertThat(personRecordClassSubject, isPresent());
@@ -290,11 +296,13 @@
hashCodeHelperClassSubject,
toStringHelperClassSubject)
.assertContainsMethodRules(
- recordTagInstanceInitializerSubject,
equalsHelperMethodSubject,
getFieldsAsObjectsMethodSubject,
hashCodeHelperMethodSubject,
- toStringHelperMethodSubject))
+ toStringHelperMethodSubject)
+ .applyIf(
+ !canHaveNonReboundConstructorInvoke,
+ j -> j.assertContainsMethodRule(recordTagInstanceInitializerSubject)))
.assertContainsNoOtherRules();
}
}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
index 385f94b..ef6b3a3 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
@@ -68,6 +68,7 @@
public void inspect(
ArtProfileInspector profileInspector,
CodeInspector inspector,
+ boolean canHaveNonReboundConstructorInvoke,
boolean canUseLambdas,
boolean canAccessModifyLambdaImplementationMethods) {
ClassSubject mainClassSubject = inspector.clazz(Main.class);
@@ -106,7 +107,9 @@
assertThat(lambdaClassSubject, notIf(isPresent(), canUseLambdas));
MethodSubject lambdaInitializerSubject = lambdaClassSubject.uniqueInstanceInitializer();
- assertThat(lambdaInitializerSubject, notIf(isPresent(), canUseLambdas));
+ assertThat(
+ lambdaInitializerSubject,
+ notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseLambdas));
MethodSubject lambdaMainMethodSubject =
lambdaClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
@@ -119,7 +122,9 @@
MethodSubject otherLambdaInitializerSubject =
otherLambdaClassSubject.uniqueInstanceInitializer();
- assertThat(otherLambdaInitializerSubject, notIf(isPresent(), canUseLambdas));
+ assertThat(
+ otherLambdaInitializerSubject,
+ notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseLambdas));
MethodSubject otherLambdaMainMethodSubject =
otherLambdaClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
@@ -149,8 +154,12 @@
// interface method implementation does not need to be included in the profile.
profileInspector
.assertContainsClassRules(lambdaClassSubject, otherLambdaClassSubject)
- .assertContainsMethodRules(
- mainMethodSubject, lambdaInitializerSubject, otherLambdaInitializerSubject)
+ .assertContainsMethodRules(mainMethodSubject)
+ .applyIf(
+ !canHaveNonReboundConstructorInvoke,
+ i ->
+ i.assertContainsMethodRules(
+ lambdaInitializerSubject, otherLambdaInitializerSubject))
.assertContainsNoOtherRules();
break;
case IMPLEMENTATION_METHOD:
@@ -221,11 +230,16 @@
}
private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector) {
- artProfileInputOutput.inspect(profileInspector, inspector, false, true);
+ artProfileInputOutput.inspect(profileInspector, inspector, false, false, true);
}
private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector) {
- artProfileInputOutput.inspect(profileInspector, inspector, parameters.isCfRuntime(), false);
+ artProfileInputOutput.inspect(
+ profileInspector,
+ inspector,
+ parameters.canHaveNonReboundConstructorInvoke(),
+ parameters.isCfRuntime(),
+ false);
}
static class Main {
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionStackTraceTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionStackTraceTest.java
new file mode 100644
index 0000000..671d075
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionStackTraceTest.java
@@ -0,0 +1,234 @@
+// 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.retrace.api;
+
+import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.MappingPartitionMetadata;
+import com.android.tools.r8.retrace.PartitionMappingSupplier;
+import com.android.tools.r8.retrace.ProguardMapPartitioner;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.Retrace;
+import com.android.tools.r8.retrace.RetraceStackTraceContext;
+import com.android.tools.r8.retrace.RetraceStackTraceElementProxy;
+import com.android.tools.r8.retrace.RetracedMethodReference.KnownRetracedMethodReference;
+import com.android.tools.r8.retrace.StackTraceElementProxy;
+import com.android.tools.r8.retrace.StackTraceLineParser;
+import com.android.tools.r8.utils.BooleanBox;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RetracePartitionStackTraceTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public RetracePartitionStackTraceTest(TestParameters parameters) {
+ parameters.assertNoneRuntime();
+ }
+
+ private final String MAPPING =
+ StringUtils.unixLines(
+ "com.Foo -> a:",
+ " 1:1:void m1():42:42 -> a",
+ "com.Bar -> b:",
+ " 2:2:void m2():43:43 -> b",
+ "com.Baz -> c:",
+ " 3:3:void m3():44:44 -> c");
+
+ @Test
+ public void testRetrace() throws Exception {
+ TestDiagnosticMessagesImpl diagnosticMessages = new TestDiagnosticMessagesImpl();
+ Map<String, byte[]> partitions = new HashMap<>();
+ MappingPartitionMetadata metadata =
+ ProguardMapPartitioner.builder(diagnosticMessages)
+ .setProguardMapProducer(ProguardMapProducer.fromString(MAPPING))
+ .setPartitionConsumer(
+ partition -> partitions.put(partition.getKey(), partition.getPayload()))
+ .build()
+ .run();
+
+ Set<String> requestedKeys = new HashSet<>();
+ BooleanBox setPartitionCallBack = new BooleanBox();
+ PartitionMappingSupplier mappingSupplier =
+ PartitionMappingSupplier.builder()
+ .setMetadata(metadata.getBytes())
+ .setRegisterMappingPartitionCallback(requestedKeys::add)
+ .setPrepareMappingPartitionsCallback(setPartitionCallBack::set)
+ .setMappingPartitionFromKeySupplier(partitions::get)
+ .build();
+
+ Retrace<StackTraceLine, StackTraceLineProxy> retrace =
+ Retrace.<StackTraceLine, StackTraceLineProxy>builder()
+ .setMappingSupplier(mappingSupplier)
+ .setStackTraceLineParser(StackTraceLineProxy::new)
+ .setDiagnosticsHandler(diagnosticMessages)
+ .build();
+
+ List<StackTraceLine> minifiedStackTrace = new ArrayList<>();
+ minifiedStackTrace.add(StackTraceLine.parse("at a.a(SourceFile:1)"));
+ minifiedStackTrace.add(StackTraceLine.parse("at b.b(SourceFile:2)"));
+ minifiedStackTrace.add(StackTraceLine.parse("at c.c(SourceFile:3)"));
+ StackTrace.Builder retraceStackTraceBuilder = StackTrace.builder();
+ retrace
+ .retraceStackTrace(minifiedStackTrace, RetraceStackTraceContext.empty())
+ .forEach(
+ stackTraceLines -> {
+ assertEquals(1, stackTraceLines.size());
+ stackTraceLines.get(0).forEach(retraceStackTraceBuilder::add);
+ });
+ assertEquals(partitions.keySet(), requestedKeys);
+ StackTrace expectedStackTrace =
+ StackTrace.builder()
+ .add(
+ StackTraceLine.builder()
+ .setClassName("com.Foo")
+ .setMethodName("m1")
+ .setFileName("Foo.java")
+ .setLineNumber(42)
+ .build())
+ .add(
+ StackTraceLine.builder()
+ .setClassName("com.Bar")
+ .setMethodName("m2")
+ .setFileName("Bar.java")
+ .setLineNumber(43)
+ .build())
+ .add(
+ StackTraceLine.builder()
+ .setClassName("com.Baz")
+ .setMethodName("m3")
+ .setFileName("Baz.java")
+ .setLineNumber(44)
+ .build())
+ .build();
+ assertThat(expectedStackTrace, isSame(retraceStackTraceBuilder.build()));
+ }
+
+ public static class IdentityStackTraceLineParser
+ implements StackTraceLineParser<StackTraceLine, StackTraceLineProxy> {
+
+ @Override
+ public StackTraceLineProxy parse(StackTraceLine stackTraceLine) {
+ return new StackTraceLineProxy(stackTraceLine);
+ }
+ }
+
+ public static class StackTraceLineProxy
+ extends StackTraceElementProxy<StackTraceLine, StackTraceLineProxy> {
+
+ private final StackTraceLine stackTraceLine;
+
+ public StackTraceLineProxy(StackTraceLine stackTraceLine) {
+ this.stackTraceLine = stackTraceLine;
+ }
+
+ @Override
+ public boolean hasClassName() {
+ return true;
+ }
+
+ @Override
+ public boolean hasMethodName() {
+ return true;
+ }
+
+ @Override
+ public boolean hasSourceFile() {
+ return true;
+ }
+
+ @Override
+ public boolean hasLineNumber() {
+ return true;
+ }
+
+ @Override
+ public boolean hasFieldName() {
+ return false;
+ }
+
+ @Override
+ public boolean hasFieldOrReturnType() {
+ return false;
+ }
+
+ @Override
+ public boolean hasMethodArguments() {
+ return false;
+ }
+
+ @Override
+ public ClassReference getClassReference() {
+ return Reference.classFromTypeName(stackTraceLine.className);
+ }
+
+ @Override
+ public String getMethodName() {
+ return stackTraceLine.methodName;
+ }
+
+ @Override
+ public String getSourceFile() {
+ return stackTraceLine.fileName;
+ }
+
+ @Override
+ public int getLineNumber() {
+ return stackTraceLine.lineNumber;
+ }
+
+ @Override
+ public String getFieldName() {
+ return null;
+ }
+
+ @Override
+ public String getFieldOrReturnType() {
+ return null;
+ }
+
+ @Override
+ public String getMethodArguments() {
+ return null;
+ }
+
+ @Override
+ public StackTraceLine toRetracedItem(
+ RetraceStackTraceElementProxy<StackTraceLine, StackTraceLineProxy> retracedProxy,
+ boolean verbose) {
+ KnownRetracedMethodReference knownRetracedMethodReference =
+ retracedProxy.getRetracedMethod().asKnown();
+ return new StackTraceLine(
+ stackTraceLine.toString(),
+ knownRetracedMethodReference.getMethodReference().getHolderClass().getTypeName(),
+ knownRetracedMethodReference.getMethodName(),
+ retracedProxy.getSourceFile(),
+ knownRetracedMethodReference.getOriginalPositionOrDefault(0));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepClassMembersFieldTest.java b/src/test/java/com/android/tools/r8/shaking/KeepClassMembersFieldTest.java
index babd7a7..31d557e 100644
--- a/src/test/java/com/android/tools/r8/shaking/KeepClassMembersFieldTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/KeepClassMembersFieldTest.java
@@ -59,10 +59,7 @@
Bar value = new Bar();
public static void main(String[] args) {
- Foo foo = new Foo();
- if (System.currentTimeMillis() < 0) {
- System.out.println(foo);
- }
+ new Foo();
}
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java b/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java
index a19fd93..f44de8c 100644
--- a/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java
@@ -30,7 +30,7 @@
public class RetainIndirectlyReferencedConstructorShakingOnDexTest extends TestBase {
@Parameter(0)
- public boolean enableRetargetingConstructorBridgeCalls;
+ public boolean enableRetargetingOfConstructorBridgeCalls;
@Parameter(1)
public TestParameters parameters;
@@ -48,8 +48,10 @@
.addKeepMainRule(Main.class)
.addOptionsModification(
options ->
- options.testing.enableRetargetingConstructorBridgeCalls =
- enableRetargetingConstructorBridgeCalls)
+ options
+ .getRedundantBridgeRemovalOptions()
+ .setEnableRetargetingOfConstructorBridgeCalls(
+ enableRetargetingOfConstructorBridgeCalls))
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters)
.compile()
@@ -80,7 +82,7 @@
invokesMethod(
MethodReferenceUtils.instanceConstructor(
parameters.canHaveNonReboundConstructorInvoke()
- && enableRetargetingConstructorBridgeCalls
+ && enableRetargetingOfConstructorBridgeCalls
? Reference.classFromDescriptor(aClassSubject.getFinalDescriptor())
: Reference.classFromDescriptor(bClassSubject.getFinalDescriptor()))));
}
diff --git a/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java b/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
index 534bbb9..7dacf64 100644
--- a/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
@@ -5,14 +5,21 @@
package com.android.tools.r8.shaking.constructor;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeFalse;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.apimodel.ApiModelingTestHelper;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -22,48 +29,168 @@
@RunWith(Parameterized.class)
public class ForwardingConstructorUsedFromPlatformShakingOnDexTest extends TestBase {
+ private static final String APPLICATION_INFO_DESCRIPTOR = "Landroid/content/pm/ApplicationInfo;";
+ private static final String FRAGMENT_DESCRIPTOR = "Landroid/app/Fragment;";
+ private static final String ZYGOTE_PRELOAD_DESCRIPTOR = "Landroid/app/ZygotePreload;";
+
+ private static final String EXPECTED_OUTPUT =
+ StringUtils.lines(
+ "Fragment.onCreate()", "MyFragment.onCreate()", "MyZygotePreload.doPreload()");
+
+ private static List<byte[]> transformedProgramClassFileData;
+ private static List<byte[]> transformedLibraryClassFileData;
+
@Parameter(0)
+ public boolean enableModeling;
+
+ @Parameter(1)
public TestParameters parameters;
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
+ @Parameters(name = "{1}, modeling: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ transformedProgramClassFileData = getTransformedProgramClasses();
+ transformedLibraryClassFileData = getTransformedLibraryClasses();
}
@Test
- public void test() throws Exception {
+ public void testRuntime() throws Exception {
+ assumeFalse(enableModeling);
+ testForRuntime(parameters)
+ .addProgramClassFileData(transformedProgramClassFileData)
+ .addProgramClassFileData(transformedLibraryClassFileData)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testR8() throws Exception {
testForR8(parameters.getBackend())
- .addProgramClasses(Main.class, MyFragment.class)
- .addLibraryClasses(Fragment.class)
+ .addProgramClassFileData(transformedProgramClassFileData)
+ .addLibraryClassFileData(transformedLibraryClassFileData)
.addLibraryFiles(parameters.getDefaultRuntimeLibrary())
.addKeepMainRule(Main.class)
+ .applyIf(
+ !enableModeling,
+ testBuilder ->
+ testBuilder.addOptionsModification(
+ options ->
+ options
+ .getRedundantBridgeRemovalOptions()
+ .clearNoConstructorShrinkingHierarchiesForTesting()))
+ // Since Fragment is first defined in API 11.
+ .apply(ApiModelingTestHelper::disableStubbingOfClasses)
.enableNeverClassInliningAnnotations()
.setMinApi(parameters)
.compile()
.inspect(this::inspect)
- .addBootClasspathClasses(Fragment.class)
+ .addRunClasspathClassFileData(transformedLibraryClassFileData)
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("Instantiating");
+ .applyIf(
+ enableModeling || !parameters.canHaveNonReboundConstructorInvoke(),
+ runResult -> runResult.assertSuccessWithOutput(EXPECTED_OUTPUT),
+ runResult -> runResult.assertFailureWithErrorThatThrows(NoSuchMethodException.class));
+ }
+
+ private static List<byte[]> getTransformedProgramClasses() throws Exception {
+ return ImmutableList.of(
+ transformer(Main.class)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(ZygotePreload.class), ZYGOTE_PRELOAD_DESCRIPTOR)
+ .transform(),
+ transformer(MyFragment.class)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
+ .setSuper(FRAGMENT_DESCRIPTOR)
+ .transform(),
+ transformer(MyZygotePreload.class)
+ .replaceClassDescriptorInMembers(
+ descriptor(ApplicationInfo.class), APPLICATION_INFO_DESCRIPTOR)
+ .setImplementsClassDescriptors(ZYGOTE_PRELOAD_DESCRIPTOR)
+ .transform());
+ }
+
+ private static List<byte[]> getTransformedLibraryClasses() throws Exception {
+ return ImmutableList.of(
+ transformer(ApplicationInfo.class)
+ .setClassDescriptor(APPLICATION_INFO_DESCRIPTOR)
+ .transform(),
+ transformer(Fragment.class).setClassDescriptor(FRAGMENT_DESCRIPTOR).transform(),
+ transformer(Platform.class)
+ .replaceClassDescriptorInMembers(descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
+ .replaceClassDescriptorInMembers(
+ descriptor(ZygotePreload.class), ZYGOTE_PRELOAD_DESCRIPTOR)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(ApplicationInfo.class), APPLICATION_INFO_DESCRIPTOR)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(ZygotePreload.class), ZYGOTE_PRELOAD_DESCRIPTOR)
+ .transform(),
+ transformer(ZygotePreload.class)
+ .replaceClassDescriptorInMembers(
+ descriptor(ApplicationInfo.class), APPLICATION_INFO_DESCRIPTOR)
+ .setClassDescriptor(ZYGOTE_PRELOAD_DESCRIPTOR)
+ .transform());
}
private void inspect(CodeInspector inspector) {
ClassSubject myFragmentClassSubject = inspector.clazz(MyFragment.class);
assertThat(myFragmentClassSubject, isPresent());
- assertThat(myFragmentClassSubject.init(), isPresent());
+ assertThat(
+ myFragmentClassSubject.init(),
+ onlyIf(enableModeling || !parameters.canHaveNonReboundConstructorInvoke(), isPresent()));
+
+ ClassSubject myZygotePreloadClassSubject = inspector.clazz(MyZygotePreload.class);
+ assertThat(myZygotePreloadClassSubject, isPresent());
+ assertThat(
+ myZygotePreloadClassSubject.init(),
+ onlyIf(enableModeling || !parameters.canHaveNonReboundConstructorInvoke(), isPresent()));
}
+ // Library classes.
+
+ public abstract static class ApplicationInfo {}
+
public abstract static class Fragment {
- public Fragment newInstance() throws Exception {
- System.out.println("Instantiating");
- return getClass().getDeclaredConstructor().newInstance();
+ public void onCreate() {
+ System.out.println("Fragment.onCreate()");
}
}
+ public interface ZygotePreload {
+
+ void doPreload(ApplicationInfo applicationInfo);
+ }
+
+ public static class Platform {
+
+ public static void accept(Fragment fragment) throws Exception {
+ Fragment newFragment = fragment.getClass().getDeclaredConstructor().newInstance();
+ newFragment.onCreate();
+ }
+
+ public static void accept(ZygotePreload runnable) throws Exception {
+ ZygotePreload newZygotePreload = runnable.getClass().getDeclaredConstructor().newInstance();
+ newZygotePreload.doPreload(null);
+ }
+ }
+
+ // Program classes.
+
public static class Main {
public static void main(String[] args) throws Exception {
- new MyFragment().newInstance();
+ Platform.accept(new MyFragment());
+ Platform.accept(new MyZygotePreload());
}
}
@@ -71,5 +198,21 @@
public static class MyFragment extends Fragment {
public MyFragment() {}
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ System.out.println("MyFragment.onCreate()");
+ }
+ }
+
+ public static class MyZygotePreload implements ZygotePreload {
+
+ public MyZygotePreload() {}
+
+ @Override
+ public void doPreload(ApplicationInfo applicationInfo) {
+ System.out.println("MyZygotePreload.doPreload()");
+ }
}
}