Merge commit 'dd2cf8c8ad27604acfe7f4c0f1e71deeda5abc73' into dev-release
diff --git a/README.md b/README.md
index 1c5dc9f..2d24142 100644
--- a/README.md
+++ b/README.md
@@ -84,8 +84,12 @@
If your contribution is owned by your employer you need the
[Corporate Contributor License Agreement](https://cla.developers.google.com/about/google-corporate).
-Once the license agreement is in place, you can upload your patches
-using 'git cl' which is available in depot_tools. Once you have a
+Once the license agreement is in place, please send an email to
+[r8-dev@googlegroups.com](mailto:r8-dev@googlegroups.com) to be added as a
+contributor.
+
+After being added as a contributer you can upload your patches
+using `git cl` which is available in `depot_tools`. Once you have a
change that you are happy with you should make sure that it passes
all tests and then upload the change to our code review tool using:
@@ -101,7 +105,8 @@
## Getting help
-For questions, reach out to us at r8-dev@googlegroups.com.
+For questions, reach out to us at
+[r8-dev@googlegroups.com](mailto:r8-dev@googlegroups.com).
For D8, find known issues in the
[D8 issue tracker](https://issuetracker.google.com/issues?q=componentid:317603)
diff --git a/infra/config/global/generated/cr-buildbucket.cfg b/infra/config/global/generated/cr-buildbucket.cfg
index 11ad90f..f4975b7 100644
--- a/infra/config/global/generated/cr-buildbucket.cfg
+++ b/infra/config/global/generated/cr-buildbucket.cfg
@@ -39,6 +39,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -64,6 +68,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -89,6 +97,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -114,6 +126,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -139,6 +155,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -165,6 +185,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -191,6 +215,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -217,6 +245,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -243,6 +275,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -269,6 +305,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -295,6 +335,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -321,6 +365,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -347,6 +395,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -373,6 +425,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -399,6 +455,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -425,6 +485,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -451,6 +515,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -477,6 +545,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -503,6 +575,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -529,6 +605,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -555,6 +635,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -581,6 +665,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -607,6 +695,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -633,6 +725,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -659,6 +755,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -685,6 +785,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -711,6 +815,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -738,6 +846,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -765,6 +877,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -791,6 +907,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -817,6 +937,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -843,6 +967,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -869,6 +997,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -895,6 +1027,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -921,6 +1057,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -946,6 +1086,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -972,6 +1116,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -998,6 +1146,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -1024,6 +1176,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -1050,6 +1206,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -1076,6 +1236,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -1102,6 +1266,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -1127,6 +1295,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
@@ -1152,6 +1324,10 @@
build_numbers: YES
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ experiments {
key: "luci.use_realms"
value: 100
}
diff --git a/infra/config/global/generated/project.cfg b/infra/config/global/generated/project.cfg
index 97e5a27..1659d63 100644
--- a/infra/config/global/generated/project.cfg
+++ b/infra/config/global/generated/project.cfg
@@ -6,3 +6,9 @@
name: "r8"
access: "group:all"
+lucicfg {
+ version: "1.29.1"
+ package_dir: ".."
+ config_dir: "generated"
+ entry_point: "main.star"
+}
diff --git a/infra/config/global/main.star b/infra/config/global/main.star
index 0d94768..238f87c 100755
--- a/infra/config/global/main.star
+++ b/infra/config/global/main.star
@@ -6,7 +6,10 @@
lucicfg.enable_experiment("crbug.com/1085650")
# Launch 0% of Builds in "realms-aware mode"
-luci.builder.defaults.experiments.set({"luci.use_realms": 100})
+luci.builder.defaults.experiments.set({
+ "luci.use_realms": 100,
+ "luci.recipes.use_python3": 100
+})
luci.project(
diff --git a/src/main/java/com/android/tools/r8/graph/ImmediateProgramSubtypingInfo.java b/src/main/java/com/android/tools/r8/graph/ImmediateProgramSubtypingInfo.java
index 07102c1..e57f798 100644
--- a/src/main/java/com/android/tools/r8/graph/ImmediateProgramSubtypingInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/ImmediateProgramSubtypingInfo.java
@@ -63,6 +63,17 @@
});
}
+ public void forEachImmediateSuperClassMatching(
+ DexClass clazz, Predicate<? super DexClass> predicate, Consumer<? super DexClass> consumer) {
+ clazz.forEachImmediateSupertype(
+ supertype -> {
+ DexClass superclass = appView.definitionFor(supertype);
+ if (superclass != null && predicate.test(superclass)) {
+ consumer.accept(superclass);
+ }
+ });
+ }
+
public void forEachImmediateProgramSuperClass(
DexProgramClass clazz, Consumer<? super DexProgramClass> consumer) {
forEachImmediateProgramSuperClassMatching(clazz, alwaysTrue(), consumer);
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 eea6e6b..e68cc71 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
@@ -275,14 +275,25 @@
}
public void addInstancePut(DexField field, InstanceFieldInitializationInfo value) {
+ if (parentConstructor == null) {
+ instanceFieldAssignmentsPre.put(field, value);
+ return;
+ }
+
// If the parent constructor is java.lang.Object.<init>() then group all the field assignments
// before the parent constructor call to allow more sharing.
- if (parentConstructor == null
- || parentConstructor == dexItemFactory.objectMembers.constructor) {
- instanceFieldAssignmentsPre.put(field, value);
- } else {
- instanceFieldAssignmentsPost.put(field, value);
+ //
+ // Note that field assignments that store the receiver cannot be hoisted to before the
+ // Object.<init>() call, since this would lead to an illegal use of the uninitialized 'this'.
+ if (parentConstructor == dexItemFactory.objectMembers.constructor) {
+ if (!value.isArgumentInitializationInfo()
+ || value.asArgumentInitializationInfo().getArgumentIndex() != 0) {
+ instanceFieldAssignmentsPre.put(field, value);
+ return;
+ }
}
+
+ instanceFieldAssignmentsPost.put(field, value);
}
public boolean addInvokeConstructor(
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
index 162ad98..fb5cfa3 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.policies.AllInstantiatedOrUninstantiated;
import com.android.tools.r8.horizontalclassmerging.policies.CheckAbstractClasses;
@@ -38,6 +39,7 @@
import com.android.tools.r8.horizontalclassmerging.policies.NoServiceLoaders;
import com.android.tools.r8.horizontalclassmerging.policies.NoVerticallyMergedClasses;
import com.android.tools.r8.horizontalclassmerging.policies.NoVirtualMethodMerging;
+import com.android.tools.r8.horizontalclassmerging.policies.NoWeakerAccessPrivileges;
import com.android.tools.r8.horizontalclassmerging.policies.NotMatchedByNoHorizontalClassMerging;
import com.android.tools.r8.horizontalclassmerging.policies.OnlyDirectlyConnectedOrUnrelatedInterfaces;
import com.android.tools.r8.horizontalclassmerging.policies.PreserveMethodCharacteristics;
@@ -194,6 +196,8 @@
Mode mode,
RuntimeTypeCheckInfo runtimeTypeCheckInfo,
ImmutableList.Builder<Policy> builder) {
+ ImmediateProgramSubtypingInfo immediateSubtypingInfo =
+ ImmediateProgramSubtypingInfo.create(appView);
builder.add(
new CheckAbstractClasses(appView),
new NoClassAnnotationCollisions(),
@@ -206,7 +210,8 @@
new RespectPackageBoundaries(appView),
new NoDifferentApiReferenceLevel(appView),
new NoIndirectRuntimeTypeChecks(appView, runtimeTypeCheckInfo),
- new PreventClassMethodAndDefaultMethodCollisions(appView));
+ new NoWeakerAccessPrivileges(appView, immediateSubtypingInfo),
+ new PreventClassMethodAndDefaultMethodCollisions(appView, immediateSubtypingInfo));
}
private static void addMultiClassPoliciesForMergingNonSyntheticClasses(
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoWeakerAccessPrivileges.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoWeakerAccessPrivileges.java
new file mode 100644
index 0000000..399e807
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoWeakerAccessPrivileges.java
@@ -0,0 +1,172 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.horizontalclassmerging.policies;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
+import com.android.tools.r8.horizontalclassmerging.MergeGroup;
+import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
+import com.android.tools.r8.optimize.argumentpropagation.utils.ProgramClassesBidirectedGraph;
+import com.android.tools.r8.utils.collections.DexMethodSignatureSet;
+import com.google.common.collect.Sets;
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class NoWeakerAccessPrivileges extends MultiClassPolicy {
+
+ private final ProgramClassesBidirectedGraph graph;
+ private final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
+
+ private final Map<DexClass, DexMethodSignatureSet> inheritedInterfaceMethodsCache =
+ new IdentityHashMap<>();
+ private final Map<Set<DexProgramClass>, DexMethodSignatureSet>
+ nonPublicVirtualMethodSignaturesCache = new IdentityHashMap<>();
+ private final Map<DexProgramClass, Set<DexProgramClass>> stronglyConnectedComponentsCache =
+ new IdentityHashMap<>();
+
+ public NoWeakerAccessPrivileges(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ ImmediateProgramSubtypingInfo immediateSubtypingInfo) {
+ this.graph = new ProgramClassesBidirectedGraph(appView, immediateSubtypingInfo);
+ this.immediateSubtypingInfo = immediateSubtypingInfo;
+ }
+
+ @Override
+ public Collection<MergeGroup> apply(MergeGroup group) {
+ List<MergeGroup> newMergeGroups = new LinkedList<>();
+ Map<MergeGroup, DexMethodSignatureSet> inheritedInterfaceMethodsPerGroup =
+ new IdentityHashMap<>();
+ for (DexProgramClass clazz : group) {
+ // Find an existing merge group that the current class can be added to.
+ MergeGroup newMergeGroup = null;
+ for (MergeGroup candidateMergeGroup : newMergeGroups) {
+ DexMethodSignatureSet inheritedInterfaceMethodsInGroup =
+ inheritedInterfaceMethodsPerGroup.get(candidateMergeGroup);
+ if (canAddToGroup(clazz, candidateMergeGroup, inheritedInterfaceMethodsInGroup)) {
+ newMergeGroup = candidateMergeGroup;
+ break;
+ }
+ }
+
+ DexMethodSignatureSet inheritedInterfaceMethodsInGroup;
+ if (newMergeGroup == null) {
+ // Form a new singleton merge group from the current class.
+ newMergeGroup = new MergeGroup(clazz);
+ newMergeGroups.add(newMergeGroup);
+ inheritedInterfaceMethodsInGroup = DexMethodSignatureSet.create();
+ inheritedInterfaceMethodsPerGroup.put(newMergeGroup, inheritedInterfaceMethodsInGroup);
+ } else {
+ // Add the current class to the existing merge group.
+ newMergeGroup.add(clazz);
+ inheritedInterfaceMethodsInGroup = inheritedInterfaceMethodsPerGroup.get(newMergeGroup);
+ }
+
+ // Record that the merge group now contains the direct and indirect interface methods of the
+ // current class.
+ inheritedInterfaceMethodsInGroup.addAll(getOrComputeInheritedInterfaceMethods(clazz));
+ }
+
+ // Remove any singleton merge groups.
+ return removeTrivialGroups(newMergeGroups);
+ }
+
+ private boolean canAddToGroup(
+ DexProgramClass clazz,
+ MergeGroup group,
+ DexMethodSignatureSet inheritedInterfaceMethodsInGroup) {
+ // We need to ensure that adding class to the group is OK.
+ DexMethodSignatureSet nonPublicVirtualMethodSignaturesInClassComponent =
+ getOrComputeNonPublicVirtualMethodSignaturesInComponentOf(clazz);
+ if (nonPublicVirtualMethodSignaturesInClassComponent.containsAnyOf(
+ inheritedInterfaceMethodsInGroup)) {
+ return false;
+ }
+
+ // We need to ensure adding all classes in the group to the class is OK.
+ Set<Set<DexProgramClass>> components = Sets.newIdentityHashSet();
+ for (DexProgramClass member : group) {
+ components.add(getOrComputeStronglyConnectedComponent(member));
+ }
+ for (Set<DexProgramClass> component : components) {
+ if (getOrComputeNonPublicVirtualMethodSignaturesInComponent(component)
+ .containsAnyOf(getOrComputeInheritedInterfaceMethods(clazz))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private DexMethodSignatureSet getOrComputeInheritedInterfaceMethods(DexClass clazz) {
+ if (inheritedInterfaceMethodsCache.containsKey(clazz)) {
+ return inheritedInterfaceMethodsCache.get(clazz);
+ }
+ DexMethodSignatureSet inheritedInterfaceMethods = DexMethodSignatureSet.create();
+ immediateSubtypingInfo.forEachImmediateSuperClassMatching(
+ clazz,
+ DexClass::isInterface,
+ superclass ->
+ inheritedInterfaceMethods.addAll(getOrComputeInheritedInterfaceMethods(superclass)));
+ if (clazz.isInterface()) {
+ clazz.forEachClassMethodMatching(
+ DexEncodedMethod::belongsToVirtualPool, inheritedInterfaceMethods::add);
+ }
+ inheritedInterfaceMethodsCache.put(clazz, inheritedInterfaceMethods);
+ return inheritedInterfaceMethods;
+ }
+
+ private Set<DexProgramClass> getOrComputeStronglyConnectedComponent(DexProgramClass clazz) {
+ if (stronglyConnectedComponentsCache.containsKey(clazz)) {
+ return stronglyConnectedComponentsCache.get(clazz);
+ }
+ Set<DexProgramClass> stronglyConnectedComponent =
+ graph.computeStronglyConnectedComponent(clazz);
+ for (DexProgramClass member : stronglyConnectedComponent) {
+ stronglyConnectedComponentsCache.put(member, stronglyConnectedComponent);
+ }
+ return stronglyConnectedComponent;
+ }
+
+ private DexMethodSignatureSet getOrComputeNonPublicVirtualMethodSignaturesInComponentOf(
+ DexProgramClass clazz) {
+ return getOrComputeNonPublicVirtualMethodSignaturesInComponent(
+ getOrComputeStronglyConnectedComponent(clazz));
+ }
+
+ private DexMethodSignatureSet getOrComputeNonPublicVirtualMethodSignaturesInComponent(
+ Set<DexProgramClass> stronglyConnectedComponent) {
+ if (nonPublicVirtualMethodSignaturesCache.containsKey(stronglyConnectedComponent)) {
+ return nonPublicVirtualMethodSignaturesCache.get(stronglyConnectedComponent);
+ }
+ DexMethodSignatureSet nonPublicVirtualMethodSignatures = DexMethodSignatureSet.create();
+ for (DexProgramClass clazz : stronglyConnectedComponent) {
+ clazz.forEachProgramVirtualMethodMatching(
+ method -> method.getAccessFlags().isPackagePrivateOrProtected(),
+ nonPublicVirtualMethodSignatures::add);
+ }
+ nonPublicVirtualMethodSignaturesCache.put(
+ stronglyConnectedComponent, nonPublicVirtualMethodSignatures);
+ return nonPublicVirtualMethodSignatures;
+ }
+
+ @Override
+ public void clear() {
+ inheritedInterfaceMethodsCache.clear();
+ nonPublicVirtualMethodSignaturesCache.clear();
+ stronglyConnectedComponentsCache.clear();
+ }
+
+ @Override
+ public String getName() {
+ return "NoWeakerAccessPriviledges";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventClassMethodAndDefaultMethodCollisions.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventClassMethodAndDefaultMethodCollisions.java
index 7c53bc3..d25c21d 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventClassMethodAndDefaultMethodCollisions.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventClassMethodAndDefaultMethodCollisions.java
@@ -12,10 +12,10 @@
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.ImmediateProgramSubtypingInfo;
import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.horizontalclassmerging.MergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
-import com.android.tools.r8.horizontalclassmerging.SubtypingForrestForClasses;
import com.android.tools.r8.utils.collections.DexMethodSignatureSet;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
@@ -52,7 +52,7 @@
public class PreventClassMethodAndDefaultMethodCollisions extends MultiClassPolicy {
private final AppView<? extends AppInfoWithClassHierarchy> appView;
- private final SubtypingForrestForClasses subtypingForrestForClasses;
+ private final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
private final InterfaceDefaultSignaturesCache interfaceDefaultMethodsCache =
new InterfaceDefaultSignaturesCache();
@@ -119,16 +119,16 @@
void process(DexProgramClass clazz, DexMethodSignatureSet signatures) {
signatures.addAll(
clazz.getInterfaces(), interfaceDefaultMethodsCache::getOrComputeSignatures);
- signatures.addAll(
- subtypingForrestForClasses.getSubtypesFor(clazz), this::getOrComputeSignatures);
+ signatures.addAll(immediateSubtypingInfo.getSubclasses(clazz), this::getOrComputeSignatures);
signatures.removeAllMethods(clazz.methods());
}
}
public PreventClassMethodAndDefaultMethodCollisions(
- AppView<? extends AppInfoWithClassHierarchy> appView) {
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ ImmediateProgramSubtypingInfo immediateSubtypingInfo) {
this.appView = appView;
- this.subtypingForrestForClasses = new SubtypingForrestForClasses(appView);
+ this.immediateSubtypingInfo = immediateSubtypingInfo;
}
enum MethodCategory {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index 9413849..b2fd06d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -71,6 +71,7 @@
factory.createSynthesizedType("Ljava/lang/NoSuchMethodException;");
factory.createSynthesizedType("Ljava/lang/NullPointerException;");
factory.createSynthesizedType("Ljava/lang/NumberFormatException;");
+ factory.createSynthesizedType("Ljava/lang/OutOfMemoryError;");
factory.createSynthesizedType("Ljava/lang/Runnable;");
factory.createSynthesizedType("Ljava/lang/SecurityException;");
factory.createSynthesizedType("Ljava/lang/reflect/InvocationTargetException;");
@@ -8354,6 +8355,9 @@
CfLabel label11 = new CfLabel();
CfLabel label12 = new CfLabel();
CfLabel label13 = new CfLabel();
+ CfLabel label14 = new CfLabel();
+ CfLabel label15 = new CfLabel();
+ CfLabel label16 = new CfLabel();
return new CfCode(
method.holder,
4,
@@ -8469,6 +8473,111 @@
FrameType.initialized(options.itemFactory.intType)
}),
new ArrayDeque<>(Arrays.asList())),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.stringType,
+ options.itemFactory.createProto(options.itemFactory.intType),
+ options.itemFactory.createString("length")),
+ false),
+ new CfConstNumber(2147483647, ValueType.INT),
+ new CfLoad(ValueType.INT, 1),
+ new CfArithmeticBinop(CfArithmeticBinop.Opcode.Div, NumericType.INT),
+ new CfIfCmp(If.Type.LE, ValueType.INT, label10),
+ label8,
+ new CfNew(options.itemFactory.createType("Ljava/lang/OutOfMemoryError;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfNew(options.itemFactory.stringBuilderType),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfInvoke(
+ 183,
+ options.itemFactory.createMethod(
+ options.itemFactory.stringBuilderType,
+ options.itemFactory.createProto(options.itemFactory.voidType),
+ options.itemFactory.createString("<init>")),
+ false),
+ new CfConstString(options.itemFactory.createString("Repeating ")),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.stringBuilderType,
+ options.itemFactory.createProto(
+ options.itemFactory.stringBuilderType, options.itemFactory.stringType),
+ options.itemFactory.createString("append")),
+ false),
+ new CfLoad(ValueType.OBJECT, 0),
+ label9,
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.stringType,
+ options.itemFactory.createProto(options.itemFactory.intType),
+ options.itemFactory.createString("length")),
+ false),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.stringBuilderType,
+ options.itemFactory.createProto(
+ options.itemFactory.stringBuilderType, options.itemFactory.intType),
+ options.itemFactory.createString("append")),
+ false),
+ new CfConstString(options.itemFactory.createString(" bytes String ")),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.stringBuilderType,
+ options.itemFactory.createProto(
+ options.itemFactory.stringBuilderType, options.itemFactory.stringType),
+ options.itemFactory.createString("append")),
+ false),
+ new CfLoad(ValueType.INT, 1),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.stringBuilderType,
+ options.itemFactory.createProto(
+ options.itemFactory.stringBuilderType, options.itemFactory.intType),
+ options.itemFactory.createString("append")),
+ false),
+ new CfConstString(
+ options.itemFactory.createString(
+ " times will produce a String exceeding maximum size.")),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.stringBuilderType,
+ options.itemFactory.createProto(
+ options.itemFactory.stringBuilderType, options.itemFactory.stringType),
+ options.itemFactory.createString("append")),
+ false),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.stringBuilderType,
+ options.itemFactory.createProto(options.itemFactory.stringType),
+ options.itemFactory.createString("toString")),
+ false),
+ new CfInvoke(
+ 183,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/lang/OutOfMemoryError;"),
+ options.itemFactory.createProto(
+ options.itemFactory.voidType, options.itemFactory.stringType),
+ options.itemFactory.createString("<init>")),
+ false),
+ new CfThrow(),
+ label10,
+ new CfFrame(
+ new Int2ReferenceAVLTreeMap<>(
+ new int[] {0, 1, 2},
+ new FrameType[] {
+ FrameType.initialized(options.itemFactory.stringType),
+ FrameType.initialized(options.itemFactory.intType),
+ FrameType.initialized(options.itemFactory.intType)
+ }),
+ new ArrayDeque<>(Arrays.asList())),
new CfNew(options.itemFactory.stringBuilderType),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfLoad(ValueType.INT, 2),
@@ -8483,10 +8592,10 @@
options.itemFactory.createString("<init>")),
false),
new CfStore(ValueType.OBJECT, 3),
- label8,
+ label11,
new CfConstNumber(0, ValueType.INT),
new CfStore(ValueType.INT, 4),
- label9,
+ label12,
new CfFrame(
new Int2ReferenceAVLTreeMap<>(
new int[] {0, 1, 2, 3, 4},
@@ -8500,8 +8609,8 @@
new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.INT, 4),
new CfLoad(ValueType.INT, 1),
- new CfIfCmp(If.Type.GE, ValueType.INT, label12),
- label10,
+ new CfIfCmp(If.Type.GE, ValueType.INT, label15),
+ label13,
new CfLoad(ValueType.OBJECT, 3),
new CfLoad(ValueType.OBJECT, 0),
new CfInvoke(
@@ -8513,10 +8622,10 @@
options.itemFactory.createString("append")),
false),
new CfStackInstruction(CfStackInstruction.Opcode.Pop),
- label11,
+ label14,
new CfIinc(4, 1),
- new CfGoto(label9),
- label12,
+ new CfGoto(label12),
+ label15,
new CfFrame(
new Int2ReferenceAVLTreeMap<>(
new int[] {0, 1, 2, 3},
@@ -8536,7 +8645,7 @@
options.itemFactory.createString("toString")),
false),
new CfReturn(ValueType.OBJECT),
- label13),
+ label16),
ImmutableList.of(),
ImmutableList.of());
}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
index 9d7d84c..15a3b02 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.BiMapContainer;
import com.android.tools.r8.utils.ChainableStringConsumer;
@@ -30,8 +31,10 @@
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
public class ClassNameMapper implements ProguardMap {
@@ -41,7 +44,9 @@
}
public static class Builder extends ProguardMap.Builder {
+
private final Map<String, ClassNamingForNameMapper.Builder> mapping = new HashMap<>();
+ private LinkedHashSet<MapVersionMappingInformation> mapVersions = new LinkedHashSet<>();
private Builder() {
@@ -58,7 +63,7 @@
@Override
public ClassNameMapper build() {
- return new ClassNameMapper(buildClassNameMappings());
+ return new ClassNameMapper(buildClassNameMappings(), mapVersions);
}
private ImmutableMap<String, ClassNamingForNameMapper> buildClassNameMappings() {
@@ -68,6 +73,12 @@
(renamedName, valueBuilder) -> builder.put(renamedName, valueBuilder.build()));
return builder.build();
}
+
+ @Override
+ ProguardMap.Builder setCurrentMapVersion(MapVersionMappingInformation mapVersion) {
+ mapVersions.add(mapVersion);
+ return this;
+ }
}
public static Builder builder() {
@@ -138,9 +149,13 @@
private final ImmutableMap<String, ClassNamingForNameMapper> classNameMappings;
private BiMapContainer<String, String> nameMapping;
private final Map<Signature, Signature> signatureMap = new HashMap<>();
+ private final Set<MapVersionMappingInformation> mapVersions;
- private ClassNameMapper(ImmutableMap<String, ClassNamingForNameMapper> classNameMappings) {
+ private ClassNameMapper(
+ ImmutableMap<String, ClassNamingForNameMapper> classNameMappings,
+ Set<MapVersionMappingInformation> mapVersions) {
this.classNameMappings = classNameMappings;
+ this.mapVersions = mapVersions;
}
public Map<String, ClassNamingForNameMapper> getClassNameMappings() {
@@ -216,7 +231,7 @@
ImmutableMap.Builder<String, ClassNamingForNameMapper> builder = ImmutableMap.builder();
builder.orderEntriesByValue(Comparator.comparing(x -> x.originalName));
classNameMappings.forEach(builder::put);
- return new ClassNameMapper(builder.build());
+ return new ClassNameMapper(builder.build(), mapVersions);
}
public boolean verifyIsSorted() {
@@ -328,4 +343,8 @@
public String originalNameOf(DexType clazz) {
return deobfuscateType(clazz.descriptor.toString());
}
+
+ public Set<MapVersionMappingInformation> getMapVersions() {
+ return mapVersions;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/naming/MapVersion.java b/src/main/java/com/android/tools/r8/naming/MapVersion.java
index 9c9c8d3..87412e8 100644
--- a/src/main/java/com/android/tools/r8/naming/MapVersion.java
+++ b/src/main/java/com/android/tools/r8/naming/MapVersion.java
@@ -8,7 +8,8 @@
public enum MapVersion implements Ordered<MapVersion> {
MAP_VERSION_NONE("none"),
MAP_VERSION_1_0("1.0"),
- MAP_VERSION_EXPERIMENTAL("experimental");
+ MAP_VERSION_EXPERIMENTAL("experimental"),
+ MAP_VERSION_UNKNOWN("unknown");
public static final MapVersion STABLE = MAP_VERSION_1_0;
@@ -30,4 +31,8 @@
}
return null;
}
+
+ public boolean isUnknown() {
+ return this == MAP_VERSION_UNKNOWN;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMap.java b/src/main/java/com/android/tools/r8/naming/ProguardMap.java
index 221f064..6b95444 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMap.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMap.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.naming;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
import com.android.tools.r8.position.Position;
public interface ProguardMap {
@@ -12,6 +13,8 @@
abstract ClassNaming.Builder classNamingBuilder(
String renamedName, String originalName, Position position);
+ abstract Builder setCurrentMapVersion(MapVersionMappingInformation mapVersion);
+
abstract ProguardMap build();
}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
index 6dd1fa3..b2cb740 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.naming.mappinginformation.MappingInformation;
import com.android.tools.r8.naming.mappinginformation.MappingInformationDiagnostics;
import com.android.tools.r8.position.TextPosition;
+import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.IdentifierUtils;
import com.android.tools.r8.utils.StringUtils;
import com.google.gson.JsonObject;
@@ -232,6 +233,9 @@
info -> {
assert info.isMapVersionMappingInformation()
|| info.isUnknownJsonMappingInformation();
+ if (info.isMapVersionMappingInformation()) {
+ mapBuilder.setCurrentMapVersion(info.asMapVersionMappingInformation());
+ }
});
// Skip reading the rest of the line.
lineOffset = line.length();
@@ -298,8 +302,12 @@
if (isCommentLineWithJsonBrace()) {
final MemberNaming currentMember = activeMemberNaming;
final MappedRange currentRange = activeMappedRange;
+ // Reading global info should cause member mapping to return since we are now reading
+ // headers pertaining to what could be a concatinated file.
+ BooleanBox readGlobalInfo = new BooleanBox(false);
parseMappingInformation(
info -> {
+ readGlobalInfo.set(info.isGlobalMappingInformation());
if (currentMember == null) {
classNamingBuilder.addMappingInformation(
info,
@@ -316,6 +324,9 @@
info, conflictingInfo, lineNo)));
}
});
+ if (readGlobalInfo.isTrue()) {
+ break;
+ }
// Skip reading the rest of the line.
lineOffset = line.length();
continue;
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
index 362f5f5..524790c 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
@@ -122,7 +122,7 @@
if (mapVersion.isGreaterThan(MapVersion.MAP_VERSION_NONE)) {
builder
.append("# ")
- .append(new MapVersionMappingInformation(mapVersion).serialize())
+ .append(new MapVersionMappingInformation(mapVersion, mapVersion.getName()).serialize())
.append("\n");
}
builder.append("# " + MARKER_KEY_PG_MAP_ID + ": " + id.getId() + "\n");
diff --git a/src/main/java/com/android/tools/r8/naming/SeedMapper.java b/src/main/java/com/android/tools/r8/naming/SeedMapper.java
index dee503b..40fc12f 100644
--- a/src/main/java/com/android/tools/r8/naming/SeedMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/SeedMapper.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.Reporter;
import com.google.common.collect.ImmutableMap;
@@ -37,6 +38,7 @@
public class SeedMapper implements ProguardMap {
static class Builder extends ProguardMap.Builder {
+
final Map<String, ClassNamingForMapApplier.Builder> map = new HashMap<>();
final Set<String> mappedToDescriptorNames = new HashSet<>();
private final Reporter reporter;
@@ -61,6 +63,12 @@
}
@Override
+ ProguardMap.Builder setCurrentMapVersion(MapVersionMappingInformation mapVersion) {
+ // Do nothing
+ return this;
+ }
+
+ @Override
SeedMapper build() {
reporter.failIfPendingErrors();
return new SeedMapper(ImmutableMap.copyOf(map), mappedToDescriptorNames, reporter);
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MapVersionMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MapVersionMappingInformation.java
index 357be56..32f11f4 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/MapVersionMappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MapVersionMappingInformation.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.naming.mappinginformation.MappingInformationDiagnostics.noKeyForObjectWithId;
-import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.naming.MapVersion;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
@@ -18,9 +17,11 @@
public static final String MAP_VERSION_KEY = "version";
private final MapVersion mapVersion;
+ private final String value;
- public MapVersionMappingInformation(MapVersion mapVersion) {
+ public MapVersionMappingInformation(MapVersion mapVersion, String value) {
this.mapVersion = mapVersion;
+ this.value = value;
}
@Override
@@ -47,6 +48,15 @@
return mapVersion;
}
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public boolean isGlobalMappingInformation() {
+ return true;
+ }
+
@Override
public String serialize() {
JsonObject result = new JsonObject();
@@ -56,11 +66,7 @@
}
public static void deserialize(
- MapVersion ignoredCurrentMapVersion,
- JsonObject object,
- DiagnosticsHandler diagnosticsHandler,
- int lineNumber,
- Consumer<MappingInformation> onMappingInfo) {
+ JsonObject object, int lineNumber, Consumer<MappingInformation> onMappingInfo) {
// Parsing the generator information must support parsing at all map versions as it itself is
// what establishes the version.
String mapVersionString = object.get(MAP_VERSION_KEY).getAsString();
@@ -70,8 +76,8 @@
}
MapVersion mapVersion = MapVersion.fromName(mapVersionString);
if (mapVersion == null) {
- return;
+ mapVersion = MapVersion.MAP_VERSION_UNKNOWN;
}
- onMappingInfo.accept(new MapVersionMappingInformation(mapVersion));
+ onMappingInfo.accept(new MapVersionMappingInformation(mapVersion, mapVersionString));
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
index 00a411f..787a0bc 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
@@ -70,6 +70,10 @@
return null;
}
+ public boolean isGlobalMappingInformation() {
+ return false;
+ }
+
public abstract boolean allowOther(MappingInformation information);
public static void fromJsonObject(
@@ -112,8 +116,7 @@
Consumer<MappingInformation> onMappingInfo) {
switch (id) {
case MapVersionMappingInformation.ID:
- MapVersionMappingInformation.deserialize(
- version, object, diagnosticsHandler, lineNumber, onMappingInfo);
+ MapVersionMappingInformation.deserialize(object, lineNumber, onMappingInfo);
return;
case FileNameInformation.ID:
FileNameInformation.deserialize(
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/BidirectedGraph.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/BidirectedGraph.java
index 8e76917..b7442b5 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/BidirectedGraph.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/BidirectedGraph.java
@@ -29,14 +29,14 @@
if (seen.contains(node)) {
return;
}
- Set<T> stronglyConnectedComponent = internalComputeStronglyConnectedProgramClasses(node);
+ Set<T> stronglyConnectedComponent = computeStronglyConnectedComponent(node);
stronglyConnectedComponents.add(stronglyConnectedComponent);
seen.addAll(stronglyConnectedComponent);
});
return stronglyConnectedComponents;
}
- private Set<T> internalComputeStronglyConnectedProgramClasses(T node) {
+ public Set<T> computeStronglyConnectedComponent(T node) {
WorkList<T> worklist = WorkList.newEqualityWorkList(node);
while (worklist.hasNext()) {
T current = worklist.next();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ProgramClassesBidirectedGraph.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ProgramClassesBidirectedGraph.java
index 474c445..e5d7179 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ProgramClassesBidirectedGraph.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ProgramClassesBidirectedGraph.java
@@ -4,19 +4,20 @@
package com.android.tools.r8.optimize.argumentpropagation.utils;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.function.Consumer;
public class ProgramClassesBidirectedGraph extends BidirectedGraph<DexProgramClass> {
- private final AppView<AppInfoWithLiveness> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
public ProgramClassesBidirectedGraph(
- AppView<AppInfoWithLiveness> appView, ImmediateProgramSubtypingInfo immediateSubtypingInfo) {
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ ImmediateProgramSubtypingInfo immediateSubtypingInfo) {
this.appView = appView;
this.immediateSubtypingInfo = immediateSubtypingInfo;
}
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 2b01885..14b393c 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -314,11 +314,20 @@
// The setup of a retracer should likely also follow a builder pattern instead of having
// static create methods. That would avoid the need to method overload the construction here
// and the default create would become the default build of a retracer.
- Retracer retracer =
+ RetracerImpl retracer =
RetracerImpl.create(
options.getProguardMapProducer(),
options.getDiagnosticsHandler(),
allowExperimentalMapping);
+ retracer
+ .getMapVersions()
+ .forEach(
+ mapVersionInfo -> {
+ if (mapVersionInfo.getMapVersion().isUnknown()) {
+ diagnosticsHandler.warning(
+ RetraceUnknownMapVersionDiagnostic.create(mapVersionInfo.getValue()));
+ }
+ });
timing.end();
timing.begin("Report result");
StringRetrace stringRetrace =
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java b/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
index c627248..b6d70e9 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
@@ -19,24 +19,14 @@
private final RetraceOptions options;
private RetraceCommand(
- String regularExpression,
- DiagnosticsHandler diagnosticsHandler,
- ProguardMapProducer proguardMapProducer,
List<String> stackTrace,
Consumer<List<String>> retracedStackTraceConsumer,
- boolean isVerbose,
- boolean verifyMappingFileHash) {
+ RetraceOptions options) {
this.stackTrace = stackTrace;
this.retracedStackTraceConsumer = retracedStackTraceConsumer;
- this.options =
- RetraceOptions.builder(diagnosticsHandler)
- .setRegularExpression(regularExpression)
- .setProguardMapProducer(proguardMapProducer)
- .setVerbose(isVerbose)
- .setVerifyMappingFileHash(verifyMappingFileHash)
- .build();
+ this.options = options;
- assert this.stackTrace != null || verifyMappingFileHash;
+ assert this.stackTrace != null || options.isVerifyMappingFileHash();
assert this.retracedStackTraceConsumer != null;
}
@@ -157,14 +147,15 @@
if (this.retracedStackTraceConsumer == null) {
throw new RuntimeException("RetracedStackConsumer not specified");
}
- return new RetraceCommand(
- regularExpression,
- diagnosticsHandler,
- proguardMapProducer,
- stackTrace,
- retracedStackTraceConsumer,
- isVerbose,
- verifyMappingFileHash);
+ RetraceOptions retraceOptions =
+ RetraceOptions.builder(diagnosticsHandler)
+ .setRegularExpression(regularExpression)
+ .setProguardMapProducer(proguardMapProducer)
+ .setVerbose(isVerbose)
+ .setVerifyMappingFileHash(verifyMappingFileHash)
+ .build();
+ return new RetraceCommand(stackTrace, retracedStackTraceConsumer, retraceOptions);
}
+
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceInvalidStackTraceLineDiagnostics.java b/src/main/java/com/android/tools/r8/retrace/RetraceInvalidStackTraceLineDiagnostics.java
index 54c8519..e18c6e6 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceInvalidStackTraceLineDiagnostics.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceInvalidStackTraceLineDiagnostics.java
@@ -14,8 +14,6 @@
public class RetraceInvalidStackTraceLineDiagnostics implements Diagnostic {
private static final String NULL_STACK_TRACE_LINE_MESSAGE = "The stack trace line is <null>";
- private static final String PARSE_STACK_TRACE_LINE_MESSAGE =
- "Could not parse the stack trace line '%s'";
private final int lineNumber;
private final String message;
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceUnknownMapVersionDiagnostic.java b/src/main/java/com/android/tools/r8/retrace/RetraceUnknownMapVersionDiagnostic.java
new file mode 100644
index 0000000..0a7a7bb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceUnknownMapVersionDiagnostic.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+
+@Keep
+public class RetraceUnknownMapVersionDiagnostic implements Diagnostic {
+
+ private final String versionName;
+
+ private RetraceUnknownMapVersionDiagnostic(String versionName) {
+ this.versionName = versionName;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+
+ @Override
+ public Position getPosition() {
+ return Position.UNKNOWN;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ return String.format(
+ "Map version '%s' is unknown or introduced later than this retrace version.", versionName);
+ }
+
+ public static RetraceUnknownMapVersionDiagnostic create(String versionName) {
+ return new RetraceUnknownMapVersionDiagnostic(versionName);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java
index 563de26..3d62354 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
@@ -17,6 +18,7 @@
import com.android.tools.r8.retrace.Retracer;
import java.io.BufferedReader;
import java.util.OptionalInt;
+import java.util.Set;
/** A default implementation for the retrace api using the ClassNameMapper defined in R8. */
public class RetracerImpl implements Retracer {
@@ -96,4 +98,8 @@
ClassReference exception, RetraceStackTraceContext context) {
return retraceClass(exception).lookupThrownException(context);
}
+
+ public Set<MapVersionMappingInformation> getMapVersions() {
+ return classNameMapper.getMapVersions();
+ }
}
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 bc0344a..7da1e8e 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -245,6 +245,7 @@
enableInitializedClassesAnalysis = false;
callSiteOptimizationOptions.disableOptimization();
horizontalClassMergerOptions.setRestrictToSynthetics();
+ apiModelTestingOptions.disableApiCallerIdentification();
}
public boolean printTimes = System.getProperty("com.android.tools.r8.printtimes") != null;
@@ -1527,6 +1528,10 @@
});
});
}
+
+ public void disableApiCallerIdentification() {
+ enableApiCallerIdentification = false;
+ }
}
public static class ProtoShrinkingOptions {
diff --git a/src/test/examplesJava11/backport/StringBackportJava11Main.java b/src/test/examplesJava11/backport/StringBackportJava11Main.java
index 6af33fa..eaf2f8a 100644
--- a/src/test/examplesJava11/backport/StringBackportJava11Main.java
+++ b/src/test/examplesJava11/backport/StringBackportJava11Main.java
@@ -25,6 +25,13 @@
assertEquals("heyhey", "hey".repeat(2));
assertEquals("heyheyhey", "hey".repeat(3));
assertEquals("heyheyheyhey", "hey".repeat(4));
+
+ try {
+ "\u03B1\u03B2".repeat(Integer.MAX_VALUE);
+ throw new AssertionError("Expected to throw OutOfMemoryError");
+ } catch (OutOfMemoryError e) {
+ // Expected.
+ }
}
/** Per {@link Character#isWhitespace(int)} */
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonPublicOverrideOfPublicMethodAfterAbstractClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonPublicOverrideOfPublicMethodAfterAbstractClassMergingTest.java
new file mode 100644
index 0000000..f540d9d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonPublicOverrideOfPublicMethodAfterAbstractClassMergingTest.java
@@ -0,0 +1,116 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPackagePrivate;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPublic;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoVerticalClassMerging;
+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 com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+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 NonPublicOverrideOfPublicMethodAfterAbstractClassMergingTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection parameters() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addHorizontallyMergedClassesInspector(
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject iClassSubject = inspector.clazz(I.class);
+ assertThat(iClassSubject, isPresent());
+ assertThat(iClassSubject.uniqueMethodWithName("m"), allOf(isPresent(), isPublic()));
+
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ assertThat(aClassSubject, not(isImplementing(iClassSubject)));
+ assertThat(
+ aClassSubject.uniqueMethodWithName("m"), allOf(isPresent(), isPackagePrivate()));
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A.m()", "Y.m()");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new B().m();
+ (System.currentTimeMillis() > 0 ? new Y() : new Z()).m();
+ }
+ }
+
+ interface I {
+
+ void m();
+ }
+
+ @NoVerticalClassMerging
+ abstract static class A {
+
+ @NeverInline
+ void m() {
+ System.out.println("A.m()");
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class B extends A {}
+
+ @NoVerticalClassMerging
+ abstract static class X implements I {}
+
+ @NoHorizontalClassMerging
+ static class Y extends X {
+
+ @Override
+ public void m() {
+ System.out.println("Y.m()");
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class Z implements I {
+
+ @Override
+ public void m() {
+ System.out.println("Z.m()");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IllegalOverrideAfterInterfaceMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IllegalOverrideAfterInterfaceMergingTest.java
index de26733..e9cd1c1 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IllegalOverrideAfterInterfaceMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IllegalOverrideAfterInterfaceMergingTest.java
@@ -4,10 +4,10 @@
package com.android.tools.r8.classmerging.horizontal.interfaces;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPackagePrivate;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPublic;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -20,6 +20,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -43,8 +44,7 @@
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
.addHorizontallyMergedClassesInspector(
- inspector ->
- inspector.assertIsCompleteMergeGroup(I.class, J.class).assertNoOtherClassesMerged())
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
@@ -56,27 +56,16 @@
inspector -> {
ClassSubject iClassSubject = inspector.clazz(I.class);
assertThat(iClassSubject, isPresent());
- assertThat(iClassSubject.uniqueMethodWithName("m"), allOf(isPresent(), isPublic()));
+ assertThat(iClassSubject.uniqueMethodWithName("m"), isAbsent());
ClassSubject aClassSubject = inspector.clazz(A.class);
assertThat(aClassSubject, isPresent());
assertThat(aClassSubject, isImplementing(iClassSubject));
- // TODO(b/203446070): Package private A.m() should not override public I.m().
assertThat(
aClassSubject.uniqueMethodWithName("m"), allOf(isPresent(), isPackagePrivate()));
})
.run(parameters.getRuntime(), Main.class)
- // TODO(b/203446070): Should always succeed.
- .applyIf(
- parameters.isCfRuntime(),
- runResult -> runResult.assertSuccessWithOutputLines("A.m()", "B.m()"),
- runResult ->
- runResult.applyIf(
- parameters.getDexRuntimeVersion().isDalvik(),
- ignore ->
- runResult.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
- ignore ->
- runResult.assertFailureWithErrorThatThrows(IllegalAccessError.class)));
+ .assertSuccessWithOutputLines("A.m()", "B.m()");
}
static class Main {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IllegalSiblingAfterInterfaceMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IllegalSiblingAfterInterfaceMergingTest.java
index a75fd7e..00a1394 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IllegalSiblingAfterInterfaceMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IllegalSiblingAfterInterfaceMergingTest.java
@@ -4,11 +4,11 @@
package com.android.tools.r8.classmerging.horizontal.interfaces;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtending;
import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPackagePrivate;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPublic;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -21,6 +21,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -44,8 +45,7 @@
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
.addHorizontallyMergedClassesInspector(
- inspector ->
- inspector.assertIsCompleteMergeGroup(I.class, J.class).assertNoOtherClassesMerged())
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
@@ -57,7 +57,7 @@
inspector -> {
ClassSubject iClassSubject = inspector.clazz(I.class);
assertThat(iClassSubject, isPresent());
- assertThat(iClassSubject.uniqueMethodWithName("m"), allOf(isPresent(), isPublic()));
+ assertThat(iClassSubject.uniqueMethodWithName("m"), isAbsent());
ClassSubject a0ClassSubject = inspector.clazz(A0.class);
assertThat(a0ClassSubject, isPresent());
@@ -70,17 +70,7 @@
assertThat(aClassSubject, isImplementing(iClassSubject));
})
.run(parameters.getRuntime(), Main.class)
- // TODO(b/203446070): Should always succeed.
- .applyIf(
- parameters.isCfRuntime(),
- runResult -> runResult.assertSuccessWithOutputLines("A.m()", "B.m()"),
- runResult ->
- runResult.applyIf(
- parameters.getDexRuntimeVersion().isDalvik(),
- ignore ->
- runResult.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
- ignore ->
- runResult.assertFailureWithErrorThatThrows(IllegalAccessError.class)));
+ .assertSuccessWithOutputLines("A.m()", "B.m()");
}
static class Main {
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/StringMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/StringMethods.java
index 9a2fc52..ce8a4a2 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/StringMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/StringMethods.java
@@ -47,6 +47,16 @@
if (count == 1) {
return receiver;
}
+ // Condition `count * receiver.length() <= Integer.MAX_VALUE` must hold for the String to be
+ // allocated.
+ if (receiver.length() > Integer.MAX_VALUE / count) {
+ throw new OutOfMemoryError(
+ "Repeating "
+ + receiver.length()
+ + " bytes String "
+ + count
+ + " times will produce a String exceeding maximum size.");
+ }
StringBuilder builder = new StringBuilder(length * count);
for (int i = 0; i < count; i++) {
builder.append(receiver);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineMappingInformationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineMappingInformationTest.java
new file mode 100644
index 0000000..1979d34
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineMappingInformationTest.java
@@ -0,0 +1,150 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.outliner;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class OutlineMappingInformationTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final boolean throwInFirstOutline;
+ private final boolean throwOnFirstCall;
+
+ @Parameterized.Parameters(name = "{0}, throwInFirstOutline: {1}, throwOnFirstCall: {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
+ BooleanUtils.values(),
+ BooleanUtils.values());
+ }
+
+ public OutlineMappingInformationTest(
+ TestParameters parameters, boolean throwInFirstOutline, boolean throwOnFirstCall) {
+ this.parameters = parameters;
+ this.throwInFirstOutline = throwInFirstOutline;
+ this.throwOnFirstCall = throwOnFirstCall;
+ }
+
+ StackTrace expectedStackTrace;
+
+ @Before
+ public void setup() throws Exception {
+ expectedStackTrace =
+ testForRuntime(parameters)
+ .addProgramClasses(TestClass.class, TestClass2.class, Greeter.class)
+ .run(
+ parameters.getRuntime(),
+ TestClass.class,
+ throwInFirstOutline ? "0" : "1",
+ throwOnFirstCall ? "0" : "1")
+ .assertFailureWithErrorThatThrows(ArrayStoreException.class)
+ .getStackTrace();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class, TestClass2.class, Greeter.class)
+ .addKeepMainRule(TestClass.class)
+ .addOptionsModification(
+ options -> {
+ options.outline.threshold = 2;
+ options.outline.minSize = 2;
+ })
+ .addKeepAttributeLineNumberTable()
+ .addKeepAttributeSourceFile()
+ .enableNoHorizontalClassMergingAnnotations()
+ .noHorizontalClassMergingOfSynthetics()
+ .addHorizontallyMergedClassesInspector(
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(
+ parameters.getRuntime(),
+ TestClass.class,
+ throwInFirstOutline ? "0" : "1",
+ throwOnFirstCall ? "0" : "1")
+ .assertFailureWithErrorThatThrows(ArrayStoreException.class)
+ .inspectStackTrace(
+ (stackTrace, inspector) -> {
+ // Two outlines are created, one for
+ // Greeter.throwExceptionFirst();
+ // Greeter.throwExceptionSecond();
+ // and one for
+ // new ArrayStoreException("Foo")
+ assertEquals(5, inspector.allClasses().size());
+ // TODO(b/201397823): Should always be equal.
+ if (throwInFirstOutline && !throwOnFirstCall) {
+ assertNotEquals(expectedStackTrace, stackTrace);
+ } else {
+ assertEquals(expectedStackTrace, stackTrace);
+ }
+ });
+ }
+
+ @NoHorizontalClassMerging
+ static class TestClass {
+
+ public static boolean shouldThrowInGreeter;
+ public static boolean throwOnFirst;
+
+ public static void main(String... args) {
+ shouldThrowInGreeter = args[0].equals("0");
+ throwOnFirst = args[1].equals("0");
+ greet();
+ shouldThrowInGreeter = true;
+ TestClass2.greet();
+ }
+
+ @NeverInline
+ static void greet() {
+ Greeter.throwExceptionFirst();
+ Greeter.throwExceptionSecond();
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class TestClass2 {
+
+ @NeverInline
+ static void greet() {
+ // Keep on same line
+ Greeter.throwExceptionFirst(); Greeter.throwExceptionSecond();
+ }
+ }
+
+ @NoHorizontalClassMerging
+ public static class Greeter {
+
+ @NeverInline
+ public static void throwExceptionFirst() {
+ if (TestClass.shouldThrowInGreeter && TestClass.throwOnFirst) {
+ throw new ArrayStoreException("Foo");
+ }
+ }
+
+ @NeverInline
+ public static void throwExceptionSecond() {
+ if (TestClass.shouldThrowInGreeter && !TestClass.throwOnFirst) {
+ throw new ArrayStoreException("Foo");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index 16aa702..3f97cd1 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -9,8 +9,10 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.DiagnosticsMatcher;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
import com.android.tools.r8.TestParameters;
@@ -40,9 +42,11 @@
import com.android.tools.r8.retrace.stacktraces.InlineSourceFileContextStackTrace;
import com.android.tools.r8.retrace.stacktraces.InlineWithLineNumbersStackTrace;
import com.android.tools.r8.retrace.stacktraces.InvalidStackTrace;
+import com.android.tools.r8.retrace.stacktraces.MapVersionWarningStackTrace;
import com.android.tools.r8.retrace.stacktraces.MemberFieldOverlapStackTrace;
import com.android.tools.r8.retrace.stacktraces.MultipleDotsInFileNameStackTrace;
import com.android.tools.r8.retrace.stacktraces.MultipleLinesNoLineNumberStackTrace;
+import com.android.tools.r8.retrace.stacktraces.MultipleMapVersionsWarningStackTrace;
import com.android.tools.r8.retrace.stacktraces.MultipleOriginalLinesNoLineNumberStackTrace;
import com.android.tools.r8.retrace.stacktraces.NamedModuleStackTrace;
import com.android.tools.r8.retrace.stacktraces.NoObfuscatedLineNumberWithOverrideTest;
@@ -364,6 +368,31 @@
runRetraceTest(new IdentityMappingStackTrace());
}
+ @Test
+ public void testMapVersionWarningStackTrace() throws Exception {
+ // TODO(b/204289928): Internalize the diagnostics checking.
+ assumeFalse(external);
+ runRetraceTest(new MapVersionWarningStackTrace())
+ .assertOnlyWarnings()
+ .assertWarningsCount(1)
+ .assertAllWarningsMatch(
+ DiagnosticsMatcher.diagnosticType(RetraceUnknownMapVersionDiagnostic.class));
+ }
+
+ @Test
+ public void testMultipleMapVersionWarningStackTrace() throws Exception {
+ // TODO(b/204289928): Internalize the diagnostics checking.
+ assumeFalse(external);
+ runRetraceTest(new MultipleMapVersionsWarningStackTrace())
+ .assertOnlyWarnings()
+ .assertAllWarningsMatch(
+ DiagnosticsMatcher.diagnosticType(RetraceUnknownMapVersionDiagnostic.class))
+ .assertWarningsCount(2)
+ .assertWarningsMatch(
+ DiagnosticsMatcher.diagnosticMessage(containsString("98.0")),
+ DiagnosticsMatcher.diagnosticMessage(containsString("99.0")));
+ }
+
private void inspectRetraceTest(
StackTraceForTest stackTraceForTest, Consumer<Retracer> inspection) {
inspection.accept(
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/MapVersionWarningStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/MapVersionWarningStackTrace.java
new file mode 100644
index 0000000..094ebd7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/MapVersionWarningStackTrace.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.stacktraces;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+
+public class MapVersionWarningStackTrace implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException", "\tat a.a(:4)");
+ }
+
+ @Override
+ public String mapping() {
+ return StringUtils.lines(
+ "# { id: 'com.android.tools.r8.mapping', version: '99.0' }", "some.Class -> a:");
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException",
+ "\tat some.Class.a(Class.java:4)");
+ }
+
+ @Override
+ public List<String> retraceVerboseStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException",
+ "\tat some.Class.a(Class.java:4)");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 1;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/MultipleMapVersionsWarningStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/MultipleMapVersionsWarningStackTrace.java
new file mode 100644
index 0000000..63717a7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/MultipleMapVersionsWarningStackTrace.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.stacktraces;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+
+public class MultipleMapVersionsWarningStackTrace implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException", "\tat a.a(:4)");
+ }
+
+ @Override
+ public String mapping() {
+ return StringUtils.lines(
+ "# { id: 'com.android.tools.r8.mapping', version: '98.0' }",
+ "some.Class -> a:",
+ "# { id: 'com.android.tools.r8.mapping', version: '99.0' }",
+ "some.other.Class -> b:",
+ "# { id: 'com.android.tools.r8.mapping', version: '1.0' }",
+ "some.third.Class -> c:");
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException",
+ "\tat some.Class.a(Class.java:4)");
+ }
+
+ @Override
+ public List<String> retraceVerboseStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException",
+ "\tat some.Class.a(Class.java:4)");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 2;
+ }
+}