Merge commit 'e8fe8ec39fe72b9ba4c1b92abd920a828e09a2b0' into dev-release
diff --git a/build.gradle b/build.gradle
index 5153337..558919e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1938,11 +1938,6 @@
systemProperty 'runtimes', project.property('runtimes')
}
- if (project.hasProperty('horizontalClassMerging')) {
- println "NOTE: Running with horizontal class merging"
- systemProperty 'com.android.tools.r8.horizontalClassMerging', 'true'
- }
-
if (project.hasProperty('slow_tests')) {
systemProperty 'slow_tests', project.property('slow_tests')
}
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index ee1883c..cd4e40c 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -176,15 +176,6 @@
}
}
builders {
- name: "linux_horizontal"
- mixins: "linux"
- mixins: "normal"
- recipe {
- properties: "tool:r8"
- properties: "horizontal_class_merging:True"
- }
- }
- builders {
name: "linux_release"
mixins: "normal"
mixins: "linux"
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index bec8477..29b0731 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -31,11 +31,6 @@
short_name: "jdk8_9"
}
builders {
- name: "buildbucket/luci.r8.ci/linux_horizontal"
- category: "R8"
- short_name: "horizontal"
- }
- builders {
name: "buildbucket/luci.r8.ci/linux-android-4.0.4"
category: "R8"
short_name: "4.0.4"
diff --git a/infra/config/global/luci-notify.cfg b/infra/config/global/luci-notify.cfg
index 4ed7592..040d32b 100644
--- a/infra/config/global/luci-notify.cfg
+++ b/infra/config/global/luci-notify.cfg
@@ -44,11 +44,6 @@
repository: "https://r8.googlesource.com/r8"
}
builders {
- name: "linux_horizontal"
- bucket: "ci"
- repository: "https://r8.googlesource.com/r8"
- }
- builders {
name: "linux_release"
bucket: "ci"
repository: "https://r8.googlesource.com/r8"
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index 634885f..3c01353 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -30,7 +30,6 @@
triggers: "linux-jdk8"
triggers: "linux-jdk9"
triggers: "linux-jdk8_9"
- triggers: "linux_horizontal"
triggers: "linux-android-4.0.4"
triggers: "linux-android-4.4.4"
triggers: "linux-android-5.1.1"
@@ -182,21 +181,6 @@
}
}
-
-job {
- id: "linux_horizontal"
- acl_sets: "default"
- triggering_policy: {
- kind: GREEDY_BATCHING
- max_concurrent_invocations: 3
- }
- buildbucket {
- server: "cr-buildbucket.appspot.com"
- bucket: "luci.r8.ci"
- builder: "linux_horizontal"
- }
-}
-
job {
id: "linux-android-4.0.4"
acl_sets: "default"
diff --git a/src/library_desugar/desugar_jdk_libs.json b/src/library_desugar/desugar_jdk_libs.json
index 945ad60..98ceb80 100644
--- a/src/library_desugar/desugar_jdk_libs.json
+++ b/src/library_desugar/desugar_jdk_libs.json
@@ -2,9 +2,10 @@
"configuration_format_version": 3,
"group_id" : "com.tools.android",
"artifact_id" : "desugar_jdk_libs",
- "version": "1.0.12",
+ "version": "1.1.0",
"required_compilation_api_level": 26,
"synthesized_library_classes_package_prefix": "j$.",
+ "support_all_callbacks_from_library": true,
"common_flags": [
{
"api_level_below_or_equal": 25,
diff --git a/src/library_desugar/desugar_jdk_libs_alternative_3.json b/src/library_desugar/desugar_jdk_libs_alternative_3.json
new file mode 100644
index 0000000..628fc53
--- /dev/null
+++ b/src/library_desugar/desugar_jdk_libs_alternative_3.json
@@ -0,0 +1,258 @@
+{
+ "configuration_format_version": 3,
+ "group_id" : "com.tools.android",
+ "artifact_id" : "desugar_jdk_libs_alternative_3",
+ "version": "1.1.0",
+ "required_compilation_api_level": 26,
+ "synthesized_library_classes_package_prefix": "j$.",
+ "support_all_callbacks_from_library": false,
+ "common_flags": [
+ {
+ "api_level_below_or_equal": 25,
+ "wrapper_conversion": [
+ "java.time.Clock"
+ ]
+ },
+ {
+ "api_level_below_or_equal": 23,
+ "wrapper_conversion": [
+ "java.util.PrimitiveIterator$OfDouble",
+ "java.util.PrimitiveIterator$OfInt",
+ "java.util.PrimitiveIterator$OfLong",
+ "java.util.Spliterator",
+ "java.util.Spliterator$OfDouble",
+ "java.util.Spliterator$OfInt",
+ "java.util.Spliterator$OfLong",
+ "java.util.Spliterator$OfPrimitive",
+ "java.util.function.BiConsumer",
+ "java.util.function.BiFunction",
+ "java.util.function.BiPredicate",
+ "java.util.function.BinaryOperator",
+ "java.util.function.Consumer",
+ "java.util.function.DoubleBinaryOperator",
+ "java.util.function.DoubleConsumer",
+ "java.util.function.DoubleFunction",
+ "java.util.function.DoublePredicate",
+ "java.util.function.DoubleToIntFunction",
+ "java.util.function.DoubleToLongFunction",
+ "java.util.function.DoubleUnaryOperator",
+ "java.util.function.Function",
+ "java.util.function.IntBinaryOperator",
+ "java.util.function.IntConsumer",
+ "java.util.function.IntFunction",
+ "java.util.function.IntPredicate",
+ "java.util.function.IntToDoubleFunction",
+ "java.util.function.IntToLongFunction",
+ "java.util.function.IntUnaryOperator",
+ "java.util.function.LongBinaryOperator",
+ "java.util.function.LongConsumer",
+ "java.util.function.LongFunction",
+ "java.util.function.LongPredicate",
+ "java.util.function.LongToDoubleFunction",
+ "java.util.function.LongToIntFunction",
+ "java.util.function.LongUnaryOperator",
+ "java.util.function.ObjDoubleConsumer",
+ "java.util.function.ObjIntConsumer",
+ "java.util.function.ObjLongConsumer",
+ "java.util.function.Predicate",
+ "java.util.function.Supplier",
+ "java.util.function.ToDoubleFunction",
+ "java.util.function.ToIntFunction",
+ "java.util.function.ToLongFunction",
+ "java.util.function.UnaryOperator",
+ "java.util.stream.BaseStream",
+ "java.util.stream.Collector",
+ "java.util.stream.DoubleStream",
+ "java.util.stream.IntStream",
+ "java.util.stream.LongStream",
+ "java.util.stream.Stream"
+ ]
+ }
+ ],
+ "library_flags": [
+ {
+ "api_level_below_or_equal": 25,
+ "rewrite_prefix": {
+ "j$.time.": "java.time.",
+ "java.time.": "j$.time.",
+ "java.util.Desugar": "j$.util.Desugar"
+ },
+ "backport": {
+ "java.lang.Double8": "java.lang.Double",
+ "java.lang.Integer8": "java.lang.Integer",
+ "java.lang.Long8": "java.lang.Long",
+ "java.lang.Math8": "java.lang.Math"
+ },
+ "retarget_lib_member": {
+ "java.util.Date#toInstant": "java.util.DesugarDate",
+ "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar",
+ "java.util.TimeZone#toZoneId": "java.util.DesugarTimeZone"
+ },
+ "custom_conversion": {
+ "java.time.ZonedDateTime": "java.time.TimeConversions",
+ "java.time.LocalDate": "java.time.TimeConversions",
+ "java.time.Duration": "java.time.TimeConversions",
+ "java.time.ZoneId": "java.time.TimeConversions",
+ "java.time.MonthDay": "java.time.TimeConversions",
+ "java.time.Instant": "java.time.TimeConversions"
+ }
+ },
+ {
+ "api_level_below_or_equal": 23,
+ "rewrite_prefix": {
+ "j$.util.Optional": "java.util.Optional",
+ "j$.util.LongSummaryStatistics": "java.util.LongSummaryStatistics",
+ "j$.util.IntSummaryStatistics": "java.util.IntSummaryStatistics",
+ "j$.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatistics",
+ "java.util.stream.": "j$.util.stream.",
+ "java.util.function.": "j$.util.function.",
+ "java.util.Comparators": "j$.util.Comparators",
+ "java.util.DoubleSummaryStatistics": "j$.util.DoubleSummaryStatistics",
+ "java.util.IntSummaryStatistics": "j$.util.IntSummaryStatistics",
+ "java.util.LongSummaryStatistics": "j$.util.LongSummaryStatistics",
+ "java.util.Objects": "j$.util.Objects",
+ "java.util.Optional": "j$.util.Optional",
+ "java.util.PrimitiveIterator": "j$.util.PrimitiveIterator",
+ "java.util.SortedSet$1": "j$.util.SortedSet$1",
+ "java.util.Spliterator": "j$.util.Spliterator",
+ "java.util.StringJoiner": "j$.util.StringJoiner",
+ "java.util.Tripwire": "j$.util.Tripwire",
+ "java.util.concurrent.DesugarUnsafe": "j$.util.concurrent.DesugarUnsafe",
+ "java.util.concurrent.ThreadLocalRandom": "j$.util.concurrent.ThreadLocalRandom",
+ "java.util.concurrent.atomic.DesugarAtomic": "j$.util.concurrent.atomic.DesugarAtomic",
+ "java.util.concurrent.ConcurrentHashMap": "j$.util.concurrent.ConcurrentHashMap",
+ "java.io.DesugarBufferedReader": "j$.io.DesugarBufferedReader",
+ "java.io.UncheckedIOException": "j$.io.UncheckedIOException"
+ },
+ "retarget_lib_member": {
+ "java.util.Arrays#stream": "java.util.DesugarArrays",
+ "java.util.Arrays#spliterator": "java.util.DesugarArrays",
+ "java.util.LinkedHashSet#spliterator": "java.util.DesugarLinkedHashSet",
+ "java.io.BufferedReader#lines": "java.io.DesugarBufferedReader"
+ },
+ "dont_rewrite": [
+ "java.util.Iterator#remove"
+ ],
+ "emulate_interface": {
+ "java.util.Map$Entry": "j$.util.Map$Entry",
+ "java.util.Collection": "j$.util.Collection",
+ "java.util.Map": "j$.util.Map",
+ "java.util.Iterator": "j$.util.Iterator",
+ "java.util.Comparator": "j$.util.Comparator",
+ "java.util.List": "j$.util.List",
+ "java.util.SortedSet": "j$.util.SortedSet",
+ "java.util.Set": "j$.util.Set",
+ "java.util.concurrent.ConcurrentMap": "j$.util.concurrent.ConcurrentMap"
+ },
+ "custom_conversion": {
+ "java.util.Optional": "java.util.OptionalConversions",
+ "java.util.OptionalDouble": "java.util.OptionalConversions",
+ "java.util.OptionalInt": "java.util.OptionalConversions",
+ "java.util.OptionalLong": "java.util.OptionalConversions",
+ "java.util.LongSummaryStatistics": "java.util.LongSummaryStatisticsConversions",
+ "java.util.IntSummaryStatistics": "java.util.IntSummaryStatisticsConversions",
+ "java.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatisticsConversions"
+ }
+ }
+ ],
+ "program_flags": [
+ {
+ "api_level_below_or_equal": 25,
+ "rewrite_prefix": {
+ "java.time.": "j$.time.",
+ "java.util.Desugar": "j$.util.Desugar"
+ },
+ "retarget_lib_member": {
+ "java.util.Calendar#toInstant": "java.util.DesugarCalendar",
+ "java.util.Date#from": "java.util.DesugarDate",
+ "java.util.Date#toInstant": "java.util.DesugarDate",
+ "java.util.GregorianCalendar#from": "java.util.DesugarGregorianCalendar",
+ "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar",
+ "java.util.TimeZone#getTimeZone": "java.util.DesugarTimeZone",
+ "java.util.TimeZone#toZoneId": "java.util.DesugarTimeZone"
+ },
+ "custom_conversion": {
+ "java.time.ZonedDateTime": "java.time.TimeConversions",
+ "java.time.LocalDate": "java.time.TimeConversions",
+ "java.time.Duration": "java.time.TimeConversions",
+ "java.time.ZoneId": "java.time.TimeConversions",
+ "java.time.MonthDay": "java.time.TimeConversions",
+ "java.time.Instant": "java.time.TimeConversions"
+ }
+ },
+ {
+ "api_level_below_or_equal": 23,
+ "rewrite_prefix": {
+ "java.util.stream.": "j$.util.stream.",
+ "java.util.function.": "j$.util.function.",
+ "java.util.DoubleSummaryStatistics": "j$.util.DoubleSummaryStatistics",
+ "java.util.IntSummaryStatistics": "j$.util.IntSummaryStatistics",
+ "java.util.LongSummaryStatistics": "j$.util.LongSummaryStatistics",
+ "java.util.Optional": "j$.util.Optional",
+ "java.util.PrimitiveIterator": "j$.util.PrimitiveIterator",
+ "java.util.Spliterator": "j$.util.Spliterator",
+ "java.util.StringJoiner": "j$.util.StringJoiner",
+ "java.util.concurrent.ThreadLocalRandom": "j$.util.concurrent.ThreadLocalRandom",
+ "java.util.concurrent.atomic.DesugarAtomic": "j$.util.concurrent.atomic.DesugarAtomic",
+ "java.util.concurrent.ConcurrentHashMap": "j$.util.concurrent.ConcurrentHashMap",
+ "java.io.UncheckedIOException": "j$.io.UncheckedIOException"
+ },
+ "retarget_lib_member": {
+ "java.util.Arrays#stream": "java.util.DesugarArrays",
+ "java.util.Arrays#spliterator": "java.util.DesugarArrays",
+ "java.util.LinkedHashSet#spliterator": "java.util.DesugarLinkedHashSet",
+ "java.util.concurrent.atomic.AtomicInteger#getAndUpdate": "java.util.concurrent.atomic.DesugarAtomicInteger",
+ "java.util.concurrent.atomic.AtomicInteger#updateAndGet": "java.util.concurrent.atomic.DesugarAtomicInteger",
+ "java.util.concurrent.atomic.AtomicInteger#getAndAccumulate": "java.util.concurrent.atomic.DesugarAtomicInteger",
+ "java.util.concurrent.atomic.AtomicInteger#accumulateAndGet": "java.util.concurrent.atomic.DesugarAtomicInteger",
+ "java.util.concurrent.atomic.AtomicLong#getAndUpdate": "java.util.concurrent.atomic.DesugarAtomicLong",
+ "java.util.concurrent.atomic.AtomicLong#updateAndGet": "java.util.concurrent.atomic.DesugarAtomicLong",
+ "java.util.concurrent.atomic.AtomicLong#getAndAccumulate": "java.util.concurrent.atomic.DesugarAtomicLong",
+ "java.util.concurrent.atomic.AtomicLong#accumulateAndGet": "java.util.concurrent.atomic.DesugarAtomicLong",
+ "java.util.concurrent.atomic.AtomicReference#getAndUpdate": "java.util.concurrent.atomic.DesugarAtomicReference",
+ "java.util.concurrent.atomic.AtomicReference#updateAndGet": "java.util.concurrent.atomic.DesugarAtomicReference",
+ "java.util.concurrent.atomic.AtomicReference#getAndAccumulate": "java.util.concurrent.atomic.DesugarAtomicReference",
+ "java.util.concurrent.atomic.AtomicReference#accumulateAndGet": "java.util.concurrent.atomic.DesugarAtomicReference",
+ "java.util.Collections#synchronizedMap": "java.util.DesugarCollections",
+ "java.util.Collections#synchronizedSortedMap": "java.util.DesugarCollections",
+ "java.io.BufferedReader#lines": "java.io.DesugarBufferedReader"
+ },
+ "dont_rewrite": [
+ "java.util.Iterator#remove"
+ ],
+ "emulate_interface": {
+ "java.util.Map$Entry": "j$.util.Map$Entry",
+ "java.util.Collection": "j$.util.Collection",
+ "java.util.Map": "j$.util.Map",
+ "java.util.Iterator": "j$.util.Iterator",
+ "java.util.Comparator": "j$.util.Comparator",
+ "java.util.List": "j$.util.List",
+ "java.util.SortedSet": "j$.util.SortedSet",
+ "java.util.Set": "j$.util.Set",
+ "java.util.concurrent.ConcurrentMap": "j$.util.concurrent.ConcurrentMap"
+ },
+ "custom_conversion": {
+ "java.util.Optional": "java.util.OptionalConversions",
+ "java.util.OptionalDouble": "java.util.OptionalConversions",
+ "java.util.OptionalInt": "java.util.OptionalConversions",
+ "java.util.OptionalLong": "java.util.OptionalConversions",
+ "java.util.LongSummaryStatistics": "java.util.LongSummaryStatisticsConversions",
+ "java.util.IntSummaryStatistics": "java.util.IntSummaryStatisticsConversions",
+ "java.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatisticsConversions"
+ }
+ }
+ ],
+ "shrinker_config": [
+ "-keepclassmembers class j$.util.concurrent.ConcurrentHashMap$TreeBin { int lockState; }",
+ "-keepclassmembers class j$.util.concurrent.ConcurrentHashMap { int sizeCtl; int transferIndex; long baseCount; int cellsBusy; }",
+ "-keepclassmembers class j$.util.concurrent.ConcurrentHashMap$CounterCell { long value; }",
+ "-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); }",
+ "-keeppackagenames j$",
+ "-keepclassmembers class j$.util.IntSummaryStatistics { long count; long sum; int min; int max; }",
+ "-keepclassmembers class j$.util.LongSummaryStatistics { long count; long sum; long min; long max; }",
+ "-keepclassmembers class j$.util.DoubleSummaryStatistics { long count; double sum; double min; double max; }",
+ "-keepattributes Signature",
+ "-keepattributes EnclosingMethod",
+ "-keepattributes InnerClasses"
+ ]
+}
diff --git a/src/main/java/com/android/tools/r8/DataEntryResource.java b/src/main/java/com/android/tools/r8/DataEntryResource.java
index 6b8f9c5..e20e5b2 100644
--- a/src/main/java/com/android/tools/r8/DataEntryResource.java
+++ b/src/main/java/com/android/tools/r8/DataEntryResource.java
@@ -26,6 +26,15 @@
return new ByteDataEntryResource(bytes, name, origin);
}
+ static DataEntryResource fromString(String name, Origin origin, String... lines) {
+ StringBuilder sb = new StringBuilder();
+ for (String line : lines) {
+ sb.append(line);
+ sb.append(System.lineSeparator());
+ }
+ return new ByteDataEntryResource(sb.toString().getBytes(), name, origin);
+ }
+
static DataEntryResource fromFile(Path dir, Path file) {
return new LocalDataEntryResource(dir.resolve(file).toFile(),
file.toString().replace(File.separatorChar, SEPARATOR));
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index dad6c63..ebc0bc6 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -532,7 +532,7 @@
mainDexTracingResult);
VerticalClassMergerGraphLens lens = verticalClassMerger.run();
if (lens != null) {
- appView.setVerticallyMergedClasses(verticalClassMerger.getMergedClasses());
+ appView.setVerticallyMergedClasses(lens.getMergedClasses());
appView.rewriteWithLens(lens);
runtimeTypeCheckInfo = runtimeTypeCheckInfo.rewriteWithLens(lens);
}
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index f9c2050..bbe9387 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -861,11 +861,16 @@
internal.enableClassStaticizer = false;
internal.outline.enabled = false;
internal.enableEnumUnboxing = false;
+ internal.enableLambdaMerging = false;
}
if (!internal.isShrinking()) {
- // If R8 is not shrinking, there is no point in unboxing enums since the unboxed enums
- // will still remain in the program (The application size would actually increase).
+ // If R8 is not shrinking, there is no point in running various optimizations since the
+ // optimized classes will still remain in the program (the application size could increase).
internal.enableEnumUnboxing = false;
+ internal.enableHorizontalClassMerging = false;
+ internal.enableLambdaMerging = false;
+ internal.enableStaticClassMerging = false;
+ internal.enableVerticalClassMerging = false;
}
if (!internal.enableInlining) {
@@ -929,6 +934,7 @@
if (internal.isGeneratingClassFiles()) {
internal.outline.enabled = false;
internal.enableEnumUnboxing = false;
+ internal.enableHorizontalClassMerging = false;
}
// EXPERIMENTAL flags.
diff --git a/src/main/java/com/android/tools/r8/errors/AssumeNoSideEffectsRuleForObjectMembersDiagnostic.java b/src/main/java/com/android/tools/r8/errors/AssumeNoSideEffectsRuleForObjectMembersDiagnostic.java
new file mode 100644
index 0000000..cb4fafb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/AssumeNoSideEffectsRuleForObjectMembersDiagnostic.java
@@ -0,0 +1,91 @@
+// Copyright (c) 2020, 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.errors;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+@Keep
+public class AssumeNoSideEffectsRuleForObjectMembersDiagnostic implements Diagnostic {
+
+ private final List<MethodReference> methods;
+ private final Origin origin;
+ private final Position position;
+
+ private AssumeNoSideEffectsRuleForObjectMembersDiagnostic(
+ List<MethodReference> methods, Origin origin, Position position) {
+ this.methods = methods;
+ this.origin = origin;
+ this.position = position;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ @Override
+ public Position getPosition() {
+ return position;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ Iterator<MethodReference> iterator = methods.iterator();
+ StringBuilder message =
+ new StringBuilder("The -assumenosideeffects rule matches the following method(s) ")
+ .append("on java.lang.Object: ")
+ .append(MethodReferenceUtils.toSourceStringWithoutHolderAndReturnType(iterator.next()));
+ while (iterator.hasNext()) {
+ MethodReference method = iterator.next();
+ message
+ .append(iterator.hasNext() ? ", " : " and ")
+ .append(MethodReferenceUtils.toSourceStringWithoutHolderAndReturnType(method));
+ }
+ return message
+ .append(". ")
+ .append("This is most likely not intended. ")
+ .append("Consider specifying the methods more precisely.")
+ .toString();
+ }
+
+ public static class Builder {
+
+ private final List<MethodReference> methods = new ArrayList<>();
+ private Origin origin;
+ private Position position;
+
+ public Builder() {}
+
+ public Builder addMatchedMethods(Set<DexMethod> methods) {
+ for (DexMethod method : methods) {
+ this.methods.add(method.asMethodReference());
+ }
+ return this;
+ }
+
+ public Builder setOrigin(Origin origin) {
+ this.origin = origin;
+ return this;
+ }
+
+ public Builder setPosition(Position position) {
+ this.position = position;
+ return this;
+ }
+
+ public AssumeNoSideEffectsRuleForObjectMembersDiagnostic build() {
+ return new AssumeNoSideEffectsRuleForObjectMembersDiagnostic(methods, origin, position);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index 114bc8a..3c5f4b7 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.InternalOptions;
import java.util.Collection;
+import java.util.List;
import java.util.function.Consumer;
public class AppInfo implements DexDefinitionSupplier {
@@ -120,7 +121,7 @@
return app.classes();
}
- public Iterable<DexProgramClass> classesWithDeterministicOrder() {
+ public List<DexProgramClass> classesWithDeterministicOrder() {
assert checkIfObsolete();
return app.classesWithDeterministicOrder();
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 877f2ba..37f45c6 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.ir.optimize.CallSiteOptimizationInfoPropagator;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoFactory;
import com.android.tools.r8.ir.optimize.library.LibraryMemberOptimizer;
+import com.android.tools.r8.ir.optimize.library.LibraryMethodSideEffectModelCollection;
import com.android.tools.r8.optimize.MemberRebindingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepInfoCollection;
@@ -69,6 +70,9 @@
// Desugared library prefix rewriter.
public final PrefixRewritingMapper rewritePrefix;
+ // Modeling.
+ private final LibraryMethodSideEffectModelCollection libraryMethodSideEffectModelCollection;
+
// Optimizations.
private final CallSiteOptimizationInfoPropagator callSiteOptimizationInfoPropagator;
private final LibraryMemberOptimizer libraryMemberOptimizer;
@@ -108,6 +112,8 @@
}
this.libraryMemberOptimizer = new LibraryMemberOptimizer(this);
+ this.libraryMethodSideEffectModelCollection =
+ new LibraryMethodSideEffectModelCollection(dexItemFactory());
if (enableWholeProgramOptimizations() && options().protoShrinking().isProtoShrinkingEnabled()) {
this.protoShrinker = new ProtoShrinker(withLiveness());
@@ -287,6 +293,10 @@
return libraryMemberOptimizer;
}
+ public LibraryMethodSideEffectModelCollection getLibraryMethodSideEffectModelCollection() {
+ return libraryMethodSideEffectModelCollection;
+ }
+
public ProtoShrinker protoShrinker() {
return protoShrinker;
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
index ae54120..bae65e8 100644
--- a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
@@ -5,11 +5,12 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
-import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.utils.MapUtils;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
@@ -77,23 +78,11 @@
private void recordOriginalTypeNames(
DexProgramClass clazz, AppView<? extends AppInfoWithClassHierarchy> appView) {
DexType type = clazz.getType();
- VerticallyMergedClasses verticallyMergedClasses = appView.verticallyMergedClasses();
- if (verticallyMergedClasses != null && verticallyMergedClasses.hasBeenMergedIntoSubtype(type)) {
- return;
- }
- DexType original = appView.graphLens().getOriginalType(type);
- if (verticallyMergedClasses != null) {
- List<DexType> sources = verticallyMergedClasses.getSourcesFor(type);
- if (!sources.isEmpty()) {
- renamedTypeNames.put(original, type);
- sources.forEach(source -> renamedTypeNames.put(source, type));
- return;
- }
- }
-
- if (original != type) {
- renamedTypeNames.put(original, type);
+ List<DexType> originalTypes = Lists.newArrayList(appView.graphLens().getOriginalTypes(type));
+ boolean isIdentity = originalTypes.size() == 1 && originalTypes.get(0) == type;
+ if (!isIdentity) {
+ originalTypes.forEach(originalType -> renamedTypeNames.put(originalType, type));
}
}
@@ -112,6 +101,15 @@
}
@Override
+ public Iterable<DexType> getOriginalTypes(DexType type) {
+ Set<DexType> originalTypes = renamedTypeNames.getKeys(type);
+ if (originalTypes == null) {
+ return ImmutableList.of(type);
+ }
+ return originalTypes;
+ }
+
+ @Override
public DexField getOriginalFieldSignature(DexField field) {
return originalFieldSignatures.getOrDefault(field, field);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index 516edc0..abb91ed 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -103,7 +103,7 @@
return box.getClasses();
}
- public Iterable<DexProgramClass> classesWithDeterministicOrder() {
+ public List<DexProgramClass> classesWithDeterministicOrder() {
List<DexProgramClass> classes = new ArrayList<>(programClasses());
// We never actually sort by anything but the DexType, this is just here in case we ever change
// that.
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
index 115bf84..0a17134 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -12,11 +12,15 @@
assert holder.isProgramClass() == (this instanceof ProgramMethod);
}
+ public static ProgramMethod asProgramMethodOrNull(DexClassAndMethod method) {
+ return method != null ? method.asProgramMethod() : null;
+ }
+
public static DexClassAndMethod create(DexClass holder, DexEncodedMethod method) {
if (holder.isProgramClass()) {
return new ProgramMethod(holder.asProgramClass(), method);
} else if (holder.isLibraryClass()) {
- return new DexClassAndMethod(holder, method);
+ return new LibraryMethod(holder.asLibraryClass(), method);
} else {
assert holder.isClasspathClass();
return new ClasspathMethod(holder.asClasspathClass(), method);
@@ -46,6 +50,14 @@
return null;
}
+ public boolean isLibraryMethod() {
+ return false;
+ }
+
+ public LibraryMethod asLibraryMethod() {
+ return null;
+ }
+
public boolean isProgramMethod() {
return false;
}
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 ea4103f..5a1e76d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -425,6 +425,15 @@
return asProgramMethod(definitions);
}
+ public DexClassAndMethod asDexClassAndMethod(DexDefinitionSupplier definitions) {
+ assert method.holder.isClassType();
+ DexClass clazz = definitions.definitionForHolder(method);
+ if (clazz != null) {
+ return DexClassAndMethod.create(clazz, this);
+ }
+ return null;
+ }
+
public ProgramMethod asProgramMethod(DexDefinitionSupplier definitions) {
assert method.holder.isClassType();
DexProgramClass clazz = asProgramClassOrNull(definitions.definitionForHolder(method));
@@ -434,6 +443,11 @@
return null;
}
+ public static DexClassAndMethod asDexClassAndMethodOrNull(
+ DexEncodedMethod method, DexDefinitionSupplier definitions) {
+ return method != null ? method.asDexClassAndMethod(definitions) : null;
+ }
+
public static ProgramMethod asProgramMethodOrNull(
DexEncodedMethod method, DexDefinitionSupplier definitions) {
return method != null ? method.asProgramMethod(definitions) : null;
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 cf30eed..b192017 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -5,7 +5,6 @@
import static com.android.tools.r8.ir.analysis.type.ClassTypeElement.computeLeastUpperBoundOfInterfaces;
import static com.android.tools.r8.ir.optimize.ServiceLoaderRewriter.SERVICE_LOADER_CLASS_NAME;
-import static com.google.common.base.Predicates.alwaysTrue;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.dex.Marker;
@@ -31,7 +30,6 @@
import com.android.tools.r8.kotlin.Kotlin;
import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.LRUCacheTable;
-import com.android.tools.r8.utils.Pair;
import com.google.common.base.Strings;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
@@ -39,7 +37,6 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
-import com.google.common.collect.Streams;
import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
@@ -57,7 +54,6 @@
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
-import java.util.stream.Stream;
public class DexItemFactory {
@@ -109,8 +105,6 @@
public DexItemFactory() {
this.kotlin = new Kotlin(this);
- assert libraryMethodsWithReturnValueDependingOnlyOnArguments.stream()
- .allMatch(libraryMethodsWithoutSideEffects::containsKey);
}
public static boolean isInternalSentinel(DexItem item) {
@@ -604,7 +598,7 @@
}
// Boxed Boxed#valueOf(Primitive), e.g., Boolean Boolean#valueOf(B)
- private Set<DexMethod> boxedValueOfMethods() {
+ public Set<DexMethod> boxedValueOfMethods() {
return primitiveToBoxed.entrySet().stream()
.map(
entry -> {
@@ -686,31 +680,6 @@
objectsMethods.requireNonNullWithMessageSupplier,
stringMembers.valueOf);
- // We assume library methods listed here are `public`, i.e., free from visibility side effects.
- // If not, that library method should not be added here because it literally has side effects.
- public Map<DexMethod, Predicate<InvokeMethod>> libraryMethodsWithoutSideEffects =
- Streams.<Pair<DexMethod, Predicate<InvokeMethod>>>concat(
- Stream.of(new Pair<>(enumMembers.constructor, alwaysTrue())),
- Stream.of(new Pair<>(npeMethods.init, alwaysTrue())),
- Stream.of(new Pair<>(npeMethods.initWithMessage, alwaysTrue())),
- Stream.of(new Pair<>(objectMembers.constructor, alwaysTrue())),
- Stream.of(new Pair<>(objectMembers.getClass, alwaysTrue())),
- Stream.of(new Pair<>(stringMembers.hashCode, alwaysTrue())),
- mapToPredicate(classMethods.getNames, alwaysTrue()),
- mapToPredicate(
- stringBufferMethods.constructorMethods,
- stringBufferMethods::constructorInvokeIsSideEffectFree),
- mapToPredicate(
- stringBuilderMethods.constructorMethods,
- stringBuilderMethods::constructorInvokeIsSideEffectFree),
- mapToPredicate(boxedValueOfMethods(), alwaysTrue()))
- .collect(Collectors.toMap(Pair::getFirst, Pair::getSecond));
-
- private static Stream<Pair<DexMethod, Predicate<InvokeMethod>>> mapToPredicate(
- Set<DexMethod> methods, Predicate<InvokeMethod> predicate) {
- return methods.stream().map(method -> new Pair<>(method, predicate));
- }
-
// TODO(b/119596718): More idempotent methods? Any singleton accessors? E.g.,
// java.util.Calendar#getInstance(...) // 4 variants
// java.util.Locale#getDefault() // returns JVM default locale.
@@ -1130,7 +1099,10 @@
public final DexField clinitField = createField(objectType, intType, "$r8$clinit");
public final DexMethod clone;
+ public final DexMethod equals =
+ createMethod(objectType, createProto(booleanType, objectType), "equals");
public final DexMethod getClass;
+ public final DexMethod hashCode = createMethod(objectType, createProto(intType), "hashCode");
public final DexMethod constructor;
public final DexMethod finalize;
public final DexMethod toString;
@@ -1199,7 +1171,7 @@
public final DexMethod getDeclaredMethod;
public final DexMethod newInstance;
private final Set<DexMethod> getMembers;
- private final Set<DexMethod> getNames;
+ public final Set<DexMethod> getNames;
private ClassMethods() {
desiredAssertionStatus = createMethod(classDescriptor,
@@ -1561,8 +1533,6 @@
public final DexMethod toString;
private final Set<DexMethod> appendMethods;
- private final Set<DexMethod> constructorMethods;
-
private StringBuildingMethods(DexType receiver) {
DexString append = createString("append");
@@ -1611,6 +1581,8 @@
charSequenceConstructor, defaultConstructor, intConstructor, stringConstructor);
}
+ public final Set<DexMethod> constructorMethods;
+
public boolean isAppendMethod(DexMethod method) {
return appendMethods.contains(method);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index a0d4cbe..74b0fff 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -37,6 +37,10 @@
return name;
}
+ public DexType getParameter(int index) {
+ return proto.getParameter(index);
+ }
+
public DexTypeList getParameters() {
return proto.parameters;
}
@@ -219,15 +223,22 @@
@Override
public String toSourceString() {
- return toSourceString(true);
+ return toSourceString(true, true);
}
public String toSourceStringWithoutHolder() {
- return toSourceString(false);
+ return toSourceString(false, true);
}
- private String toSourceString(boolean includeHolder) {
- StringBuilder builder = new StringBuilder(proto.returnType.toSourceString()).append(" ");
+ public String toSourceStringWithoutHolderAndReturnType() {
+ return toSourceString(false, false);
+ }
+
+ private String toSourceString(boolean includeHolder, boolean includeReturnType) {
+ StringBuilder builder = new StringBuilder();
+ if (includeReturnType) {
+ builder.append(getReturnType().toSourceString()).append(" ");
+ }
if (includeHolder) {
builder.append(holder.toSourceString()).append(".");
}
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index ac9b854..eec753e 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.ir.desugar.InterfaceProcessor.InterfaceProcessorNestedGraphLens;
import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.utils.Action;
+import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.BiMap;
@@ -273,6 +274,8 @@
public abstract DexType getOriginalType(DexType type);
+ public abstract Iterable<DexType> getOriginalTypes(DexType type);
+
public abstract DexField getOriginalFieldSignature(DexField field);
public abstract DexMethod getOriginalMethodSignature(DexMethod method);
@@ -499,15 +502,16 @@
return result;
}
- public DexReference rewriteReference(DexReference reference) {
+ @SuppressWarnings("unchecked")
+ public <T extends DexReference> T rewriteReference(T reference) {
if (reference.isDexField()) {
- return getRenamedFieldSignature(reference.asDexField());
+ return (T) getRenamedFieldSignature(reference.asDexField());
}
if (reference.isDexMethod()) {
- return getRenamedMethodSignature(reference.asDexMethod());
+ return (T) getRenamedMethodSignature(reference.asDexMethod());
}
assert reference.isDexType();
- return lookupType(reference.asDexType());
+ return (T) lookupType(reference.asDexType());
}
public Set<DexReference> rewriteReferences(Set<DexReference> references) {
@@ -518,8 +522,8 @@
return result;
}
- public <T> ImmutableMap<DexReference, T> rewriteReferenceKeys(Map<DexReference, T> map) {
- ImmutableMap.Builder<DexReference, T> builder = ImmutableMap.builder();
+ public <R extends DexReference, T> ImmutableMap<R, T> rewriteReferenceKeys(Map<R, T> map) {
+ ImmutableMap.Builder<R, T> builder = ImmutableMap.builder();
map.forEach((reference, value) -> builder.put(rewriteReference(reference), value));
return builder.build();
}
@@ -761,6 +765,11 @@
}
@Override
+ public Iterable<DexType> getOriginalTypes(DexType type) {
+ return IterableUtils.singleton(type);
+ }
+
+ @Override
public DexField getOriginalFieldSignature(DexField field) {
return field;
}
@@ -845,6 +854,11 @@
}
@Override
+ public Iterable<DexType> getOriginalTypes(DexType type) {
+ return getPrevious().getOriginalTypes(type);
+ }
+
+ @Override
public DexField getOriginalFieldSignature(DexField field) {
return getPrevious().getOriginalFieldSignature(field);
}
@@ -965,9 +979,22 @@
return new Builder();
}
+ protected DexType internalGetOriginalType(DexType previous) {
+ return previous;
+ }
+
+ protected Iterable<DexType> internalGetOriginalTypes(DexType previous) {
+ return IterableUtils.singleton(internalGetOriginalType(previous));
+ }
+
@Override
public DexType getOriginalType(DexType type) {
- return getPrevious().getOriginalType(type);
+ return getPrevious().getOriginalType(internalGetOriginalType(type));
+ }
+
+ @Override
+ public Iterable<DexType> getOriginalTypes(DexType type) {
+ return IterableUtils.flatMap(internalGetOriginalTypes(type), getPrevious()::getOriginalTypes);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/LibraryMethod.java b/src/main/java/com/android/tools/r8/graph/LibraryMethod.java
new file mode 100644
index 0000000..c06ebab
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/LibraryMethod.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2019, 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.graph;
+
+/** Type representing a method definition from the library and its holder. */
+public final class LibraryMethod extends DexClassAndMethod {
+
+ public LibraryMethod(DexLibraryClass holder, DexEncodedMethod method) {
+ super(holder, method);
+ }
+
+ @Override
+ public boolean isLibraryMethod() {
+ return true;
+ }
+
+ @Override
+ public LibraryMethod asLibraryMethod() {
+ return this;
+ }
+
+ @Override
+ public DexLibraryClass getHolder() {
+ DexClass holder = super.getHolder();
+ assert holder.isLibraryClass();
+ return holder.asLibraryClass();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java b/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java
index 949fd47..7c38464 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java
@@ -95,6 +95,11 @@
}
@Override
+ void clearDirectMethods() {
+ directMethods = DexEncodedMethod.EMPTY_ARRAY;
+ }
+
+ @Override
DexEncodedMethod removeMethod(DexMethod method) {
DexEncodedMethod removedDirectMethod =
removeMethodHelper(
@@ -175,6 +180,11 @@
}
@Override
+ void clearVirtualMethods() {
+ virtualMethods = DexEncodedMethod.EMPTY_ARRAY;
+ }
+
+ @Override
void setVirtualMethods(DexEncodedMethod[] methods) {
virtualMethods = MoreObjects.firstNonNull(methods, DexEncodedMethod.EMPTY_ARRAY);
assert verifyNoDuplicateMethods();
@@ -344,4 +354,26 @@
}
}
}
+
+ @Override
+ public void replaceAllDirectMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ DexEncodedMethod[] oldMethods = directMethods;
+ clearDirectMethods();
+ DexEncodedMethod[] newMethods = new DexEncodedMethod[oldMethods.length];
+ for (int i = 0; i < oldMethods.length; i++) {
+ newMethods[i] = replacement.apply(oldMethods[i]);
+ }
+ directMethods = newMethods;
+ }
+
+ @Override
+ public void replaceAllVirtualMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ DexEncodedMethod[] oldMethods = virtualMethods;
+ clearVirtualMethods();
+ DexEncodedMethod[] newMethods = new DexEncodedMethod[oldMethods.length];
+ for (int i = 0; i < oldMethods.length; i++) {
+ newMethods[i] = replacement.apply(oldMethods[i]);
+ }
+ virtualMethods = newMethods;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollection.java b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
index 312cec2..b14ff47 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
@@ -223,14 +223,24 @@
backing.replaceMethods(replacement);
}
+ public void replaceDirectMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ resetDirectMethodCaches();
+ backing.replaceDirectMethods(replacement);
+ }
+
public void replaceVirtualMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
resetVirtualMethodCaches();
backing.replaceVirtualMethods(replacement);
}
- public void replaceDirectMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ public void replaceAllDirectMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
resetDirectMethodCaches();
- backing.replaceDirectMethods(replacement);
+ backing.replaceAllDirectMethods(replacement);
+ }
+
+ public void replaceAllVirtualMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ resetVirtualMethodCaches();
+ backing.replaceAllVirtualMethods(replacement);
}
/**
@@ -252,6 +262,11 @@
backing.addDirectMethods(methods);
}
+ public void clearDirectMethods() {
+ resetDirectMethodCaches();
+ backing.clearDirectMethods();
+ }
+
public DexEncodedMethod removeMethod(DexMethod method) {
DexEncodedMethod removed = backing.removeMethod(method);
if (removed != null) {
@@ -283,6 +298,11 @@
backing.addVirtualMethods(methods);
}
+ public void clearVirtualMethods() {
+ resetVirtualMethodCaches();
+ backing.clearVirtualMethods();
+ }
+
public void setVirtualMethods(DexEncodedMethod[] methods) {
assert verifyCorrectnessOfMethodHolders(methods);
resetVirtualMethodCaches();
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java b/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java
index 601306c..37a46f0 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodCollectionBacking.java
@@ -94,6 +94,10 @@
// Removal methods.
+ abstract void clearDirectMethods();
+
+ abstract void clearVirtualMethods();
+
abstract DexEncodedMethod removeMethod(DexMethod method);
abstract void removeMethods(Set<DexEncodedMethod> method);
@@ -108,8 +112,12 @@
abstract void replaceDirectMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement);
+ abstract void replaceAllDirectMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement);
+
abstract void replaceVirtualMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement);
+ abstract void replaceAllVirtualMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement);
+
abstract DexEncodedMethod replaceDirectMethod(
DexMethod method, Function<DexEncodedMethod, DexEncodedMethod> replacement);
diff --git a/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java b/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
index d877896..2874e03 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
@@ -8,12 +8,14 @@
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.TraversalContinuation;
import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.Lists;
import it.unimi.dsi.fastutil.objects.Object2ReferenceLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ReferenceMap;
import it.unimi.dsi.fastutil.objects.Object2ReferenceRBTreeMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
+import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Function;
@@ -195,6 +197,16 @@
}
@Override
+ void clearDirectMethods() {
+ methodMap.values().removeIf(this::belongsToDirectPool);
+ }
+
+ @Override
+ void clearVirtualMethods() {
+ methodMap.values().removeIf(this::belongsToVirtualPool);
+ }
+
+ @Override
DexEncodedMethod removeMethod(DexMethod method) {
return methodMap.remove(wrap(method));
}
@@ -276,6 +288,28 @@
}
@Override
+ void replaceAllDirectMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ List<DexEncodedMethod> oldMethods = Lists.newArrayList(directMethods());
+ clearDirectMethods();
+ List<DexEncodedMethod> newMethods = new ArrayList<>(oldMethods.size());
+ for (DexEncodedMethod method : oldMethods) {
+ newMethods.add(replacement.apply(method));
+ }
+ addDirectMethods(newMethods);
+ }
+
+ @Override
+ void replaceAllVirtualMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ List<DexEncodedMethod> oldMethods = Lists.newArrayList(virtualMethods());
+ clearVirtualMethods();
+ List<DexEncodedMethod> newMethods = new ArrayList<>(oldMethods.size());
+ for (DexEncodedMethod method : oldMethods) {
+ newMethods.add(replacement.apply(method));
+ }
+ addVirtualMethods(newMethods);
+ }
+
+ @Override
DexEncodedMethod replaceDirectMethod(
DexMethod method, Function<DexEncodedMethod, DexEncodedMethod> replacement) {
return replaceMethod(method, replacement, this::belongsToDirectPool);
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
index f76f9e7..ed69ace 100644
--- a/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
@@ -7,27 +7,28 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Maps;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Map;
+import java.util.Set;
public class VerticallyMergedClasses implements MergedClasses {
private final Map<DexType, DexType> mergedClasses;
- private final Map<DexType, List<DexType>> sources;
+ private final Map<DexType, Set<DexType>> mergedClassesInverse;
- public VerticallyMergedClasses(Map<DexType, DexType> mergedClasses) {
- Map<DexType, List<DexType>> sources = Maps.newIdentityHashMap();
- mergedClasses.forEach(
- (source, target) -> sources.computeIfAbsent(target, key -> new ArrayList<>()).add(source));
+ public VerticallyMergedClasses(
+ Map<DexType, DexType> mergedClasses, Map<DexType, Set<DexType>> mergedClassesInverse) {
this.mergedClasses = mergedClasses;
- this.sources = sources;
+ this.mergedClassesInverse = mergedClassesInverse;
}
- public List<DexType> getSourcesFor(DexType type) {
- return sources.getOrDefault(type, ImmutableList.of());
+ public Map<DexType, DexType> getForwardMap() {
+ return mergedClasses;
+ }
+
+ public Collection<DexType> getSourcesFor(DexType type) {
+ return mergedClassesInverse.getOrDefault(type, Collections.emptySet());
}
public DexType getTargetFor(DexType type) {
@@ -45,7 +46,7 @@
@Override
public boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView) {
- for (List<DexType> sourcesForTarget : sources.values()) {
+ for (Collection<DexType> sourcesForTarget : mergedClassesInverse.values()) {
for (DexType source : sourcesForTarget) {
assert appView.appInfo().wasPruned(source)
: "Expected vertically merged class `" + source.toSourceString() + "` to be absent";
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index cfd5182..a6003c4 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -28,8 +28,9 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.IdentityHashMap;
+import java.util.Comparator;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -41,7 +42,7 @@
public static final String CLASS_ID_FIELD_NAME = "$r8$classId";
private final DexProgramClass target;
- private final Collection<DexProgramClass> toMergeGroup;
+ private final List<DexProgramClass> toMergeGroup;
private final DexItemFactory dexItemFactory;
private final HorizontalClassMergerGraphLens.Builder lensBuilder;
private final HorizontallyMergedClasses.Builder mergedClassesBuilder;
@@ -58,7 +59,7 @@
HorizontallyMergedClasses.Builder mergedClassesBuilder,
FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
DexProgramClass target,
- Collection<DexProgramClass> toMergeGroup,
+ List<DexProgramClass> toMergeGroup,
DexField classIdField,
Collection<VirtualMethodMerger> virtualMethodMergers,
Collection<ConstructorMerger> constructorMergers) {
@@ -174,9 +175,9 @@
public static class Builder {
private final DexProgramClass target;
- private final Collection<DexProgramClass> toMergeGroup = new ArrayList<>();
+ private final List<DexProgramClass> toMergeGroup = new ArrayList<>();
private final Map<DexProto, ConstructorMerger.Builder> constructorMergerBuilders =
- new IdentityHashMap<>();
+ new LinkedHashMap<>();
private final Map<Wrapper<DexMethod>, VirtualMethodMerger.Builder> virtualMethodMergerBuilders =
new LinkedHashMap<>();
@@ -191,7 +192,7 @@
return this;
}
- public Builder addClassesToMerge(Collection<DexProgramClass> toMerge) {
+ public Builder addClassesToMerge(List<DexProgramClass> toMerge) {
toMerge.forEach(this::mergeClass);
return this;
}
@@ -237,18 +238,26 @@
DexField classIdField =
dexItemFactory.createField(target.type, dexItemFactory.intType, CLASS_ID_FIELD_NAME);
- Collection<VirtualMethodMerger> virtualMethodMergers =
+ List<VirtualMethodMerger> virtualMethodMergers =
new ArrayList<>(virtualMethodMergerBuilders.size());
for (VirtualMethodMerger.Builder builder : virtualMethodMergerBuilders.values()) {
virtualMethodMergers.add(builder.build(appView, target, classIdField));
}
+ // Try and merge the functions with the most arguments first, to avoid using synthetic
+ // arguments if possible.
+ virtualMethodMergers.sort(Comparator.comparing(VirtualMethodMerger::getArity).reversed());
- Collection<ConstructorMerger> constructorMergers =
+ List<ConstructorMerger> constructorMergers =
new ArrayList<>(constructorMergerBuilders.size());
for (ConstructorMerger.Builder builder : constructorMergerBuilders.values()) {
constructorMergers.add(builder.build(appView, target, classIdField));
}
+ // Try and merge the functions with the most arguments first, to avoid using synthetic
+ // arguments if possible.
+ virtualMethodMergers.sort(Comparator.comparing(VirtualMethodMerger::getArity).reversed());
+ constructorMergers.sort(Comparator.comparing(ConstructorMerger::getArity).reversed());
+
return new ClassMerger(
appView,
lensBuilder,
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
index 9f201bf..60ff636b 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
@@ -55,6 +55,10 @@
this.dexItemFactory = appView.dexItemFactory();
}
+ public int getArity() {
+ return constructors.iterator().next().getReference().getArity();
+ }
+
public static class Builder {
private final Collection<DexEncodedMethod> constructors;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index c9a40d7..c8a1c16 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.horizontalclassmerging.policies.AllInstantiatedOrUninstantiated;
import com.android.tools.r8.horizontalclassmerging.policies.DontInlinePolicy;
import com.android.tools.r8.horizontalclassmerging.policies.DontMergeIntoLessVisible;
import com.android.tools.r8.horizontalclassmerging.policies.DontMergeSynchronizedClasses;
@@ -40,7 +41,7 @@
import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -76,6 +77,7 @@
new NotEntryPoint(appView.dexItemFactory()),
new DontInlinePolicy(appView, mainDexTracingResult),
new PreventMergeIntoMainDex(appView, mainDexTracingResult),
+ new AllInstantiatedOrUninstantiated(appView),
new SameParentClass(),
new SameNestHost(),
new PreventChangingVisibility(),
@@ -93,7 +95,7 @@
// TODO(b/165577835): replace Collection<DexProgramClass> with MergeGroup
public HorizontalClassMergerGraphLens run(DirectMappedDexApplication.Builder appBuilder) {
- Map<FieldMultiset, Collection<DexProgramClass>> classes = new HashMap<>();
+ Map<FieldMultiset, List<DexProgramClass>> classes = new LinkedHashMap<>();
// Group classes by same field signature using the hash map.
for (DexProgramClass clazz : appView.appInfo().app().classesWithDeterministicOrder()) {
@@ -101,7 +103,7 @@
}
// Run the policies on all collected classes to produce a final grouping.
- Collection<Collection<DexProgramClass>> groups = policyExecutor.run(classes.values());
+ Collection<List<DexProgramClass>> groups = policyExecutor.run(classes.values());
// If there are no groups, then end horizontal class merging.
if (groups.isEmpty()) {
appView.setHorizontallyMergedClasses(HorizontallyMergedClasses.empty());
@@ -116,7 +118,7 @@
new FieldAccessInfoCollectionModifier.Builder();
// Set up a class merger for each group.
- Collection<ClassMerger> classMergers =
+ List<ClassMerger> classMergers =
initializeClassMergers(
mergedClassesBuilder, lensBuilder, fieldAccessChangesBuilder, groups);
Iterable<DexProgramClass> allMergeClasses =
@@ -138,15 +140,15 @@
* Prepare horizontal class merging by determining which virtual methods and constructors need to
* be merged and how the merging should be performed.
*/
- private Collection<ClassMerger> initializeClassMergers(
+ private List<ClassMerger> initializeClassMergers(
HorizontallyMergedClasses.Builder mergedClassesBuilder,
HorizontalClassMergerGraphLens.Builder lensBuilder,
FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
- Collection<Collection<DexProgramClass>> groups) {
- Collection<ClassMerger> classMergers = new ArrayList<>();
+ Collection<List<DexProgramClass>> groups) {
+ List<ClassMerger> classMergers = new ArrayList<>();
// TODO(b/166577694): Replace Collection<DexProgramClass> with MergeGroup
- for (Collection<DexProgramClass> group : groups) {
+ for (List<DexProgramClass> group : groups) {
assert !group.isEmpty();
DexProgramClass target = group.stream().findFirst().get();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
index 5f5a62b..867725b 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -7,9 +7,11 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.ir.conversion.ExtraParameter;
+import com.android.tools.r8.utils.IterableUtils;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import java.util.ArrayList;
@@ -24,10 +26,11 @@
private final AppView<?> appView;
private final Map<DexMethod, List<ExtraParameter>> methodExtraParameters;
private final Map<DexMethod, DexMethod> originalConstructorSignatures;
+ private final HorizontallyMergedClasses mergedClasses;
private HorizontalClassMergerGraphLens(
AppView<?> appView,
- HorizontallyMergedClasses horizontallyMergedClasses,
+ HorizontallyMergedClasses mergedClasses,
Map<DexMethod, List<ExtraParameter>> methodExtraParameters,
Map<DexField, DexField> fieldMap,
Map<DexMethod, DexMethod> methodMap,
@@ -36,7 +39,7 @@
Map<DexMethod, DexMethod> originalConstructorSignatures,
GraphLens previousLens) {
super(
- horizontallyMergedClasses.getForwardMap(),
+ mergedClasses.getForwardMap(),
methodMap,
fieldMap,
originalFieldSignatures,
@@ -46,6 +49,12 @@
this.appView = appView;
this.methodExtraParameters = methodExtraParameters;
this.originalConstructorSignatures = originalConstructorSignatures;
+ this.mergedClasses = mergedClasses;
+ }
+
+ @Override
+ protected Iterable<DexType> internalGetOriginalTypes(DexType previous) {
+ return IterableUtils.prependSingleton(previous, mergedClasses.getSourcesFor(previous));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
index 653e31a..080102a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
@@ -34,6 +34,10 @@
return mergedClasses.keySet();
}
+ public Set<DexType> getSourcesFor(DexType type) {
+ return mergedClasses.getKeys(type);
+ }
+
public boolean hasBeenMergedIntoDifferentType(DexType type) {
return mergedClasses.hasKey(type);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassPolicy.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassPolicy.java
index 829c1f3..2c8f10a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassPolicy.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassPolicy.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
public abstract class MultiClassPolicy extends Policy {
@@ -18,7 +19,7 @@
/**
* Remove all groups containing no or only a single class, as there is no point in merging these.
*/
- protected void removeTrivialGroups(Collection<Collection<DexProgramClass>> groups) {
+ protected void removeTrivialGroups(Collection<List<DexProgramClass>> groups) {
assert !(groups instanceof ArrayList);
groups.removeIf(this::isTrivial);
}
@@ -31,5 +32,5 @@
* merged. If the policy detects no issues then `group` will be returned unchanged. If classes
* cannot be merged with any other classes they are returned as singleton lists.
*/
- public abstract Collection<Collection<DexProgramClass>> apply(Collection<DexProgramClass> group);
+ public abstract Collection<List<DexProgramClass>> apply(List<DexProgramClass> group);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassSameReferencePolicy.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassSameReferencePolicy.java
index 0a7e988..bf460d9 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassSameReferencePolicy.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/MultiClassSameReferencePolicy.java
@@ -6,14 +6,15 @@
import com.android.tools.r8.graph.DexProgramClass;
import java.util.Collection;
-import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
public abstract class MultiClassSameReferencePolicy<T> extends MultiClassPolicy {
@Override
- public final Collection<Collection<DexProgramClass>> apply(Collection<DexProgramClass> group) {
- Map<T, Collection<DexProgramClass>> groups = new IdentityHashMap<>();
+ public final Collection<List<DexProgramClass>> apply(List<DexProgramClass> group) {
+ Map<T, List<DexProgramClass>> groups = new LinkedHashMap<>();
for (DexProgramClass clazz : group) {
groups.computeIfAbsent(getMergeKey(clazz), ignore -> new LinkedList<>()).add(clazz);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyExecutor.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyExecutor.java
index 7ffaa1b..cddca41 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyExecutor.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyExecutor.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import java.util.Collection;
+import java.util.List;
public abstract class PolicyExecutor {
protected final Collection<Policy> policies;
@@ -19,6 +20,5 @@
* policies registered to this policy executor on the class groups yielding a new collection of
* class groups.
*/
- public abstract Collection<Collection<DexProgramClass>> run(
- Collection<Collection<DexProgramClass>> classes);
+ public abstract Collection<List<DexProgramClass>> run(Collection<List<DexProgramClass>> classes);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java
index 6192da8..a5f26d8 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java
@@ -8,6 +8,7 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
+import java.util.List;
import java.util.stream.Collectors;
/**
@@ -21,9 +22,9 @@
}
// TODO(b/165506334): if performing mutable operation ensure that linked lists are used
- private LinkedList<Collection<DexProgramClass>> applySingleClassPolicy(
- SingleClassPolicy policy, LinkedList<Collection<DexProgramClass>> groups) {
- Iterator<Collection<DexProgramClass>> i = groups.iterator();
+ private LinkedList<List<DexProgramClass>> applySingleClassPolicy(
+ SingleClassPolicy policy, LinkedList<List<DexProgramClass>> groups) {
+ Iterator<List<DexProgramClass>> i = groups.iterator();
while (i.hasNext()) {
Collection<DexProgramClass> group = i.next();
int previousNumberOfClasses = group.size();
@@ -36,8 +37,8 @@
return groups;
}
- private LinkedList<Collection<DexProgramClass>> applyMultiClassPolicy(
- MultiClassPolicy policy, LinkedList<Collection<DexProgramClass>> groups) {
+ private LinkedList<List<DexProgramClass>> applyMultiClassPolicy(
+ MultiClassPolicy policy, LinkedList<List<DexProgramClass>> groups) {
// For each group apply the multi class policy and add all the new groups together.
return groups.stream()
.flatMap(group -> policy.apply(group).stream())
@@ -45,12 +46,11 @@
}
@Override
- public Collection<Collection<DexProgramClass>> run(
- Collection<Collection<DexProgramClass>> inputGroups) {
- LinkedList<Collection<DexProgramClass>> linkedGroups;
+ public Collection<List<DexProgramClass>> run(Collection<List<DexProgramClass>> inputGroups) {
+ LinkedList<List<DexProgramClass>> linkedGroups;
if (inputGroups instanceof LinkedList) {
- linkedGroups = (LinkedList<Collection<DexProgramClass>>) inputGroups;
+ linkedGroups = (LinkedList<List<DexProgramClass>>) inputGroups;
} else {
linkedGroups = new LinkedList<>(inputGroups);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SubtypingForrestForClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SubtypingForrestForClasses.java
index 402ddb1..b643225 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/SubtypingForrestForClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/SubtypingForrestForClasses.java
@@ -11,6 +11,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.IdentityHashMap;
+import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
@@ -32,13 +33,12 @@
private final AppView<AppInfoWithLiveness> appView;
private final Collection<DexProgramClass> roots = new ArrayList<>();
- private final Map<DexProgramClass, Collection<DexProgramClass>> subtypeMap =
- new IdentityHashMap<>();
+ private final Map<DexProgramClass, List<DexProgramClass>> subtypeMap = new IdentityHashMap<>();
- public SubtypingForrestForClasses(AppView<AppInfoWithLiveness> appView) {
+ public SubtypingForrestForClasses(
+ AppView<AppInfoWithLiveness> appView, List<DexProgramClass> classesWithDeterministicOrder) {
this.appView = appView;
-
- calculateSubtyping(appView.appInfo().classes());
+ calculateSubtyping(classesWithDeterministicOrder);
}
private DexProgramClass superClass(DexProgramClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
index b102b69..8cf6265 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -120,11 +120,11 @@
* </ul>
*/
public HorizontalClassMergerGraphLens fixupTypeReferences() {
- Iterable<DexProgramClass> classes = appView.appInfo().classesWithDeterministicOrder();
+ List<DexProgramClass> classes = appView.appInfo().classesWithDeterministicOrder();
Iterables.filter(classes, DexProgramClass::isInterface).forEach(this::fixupInterfaceClass);
classes.forEach(this::fixupProgramClassSuperType);
- SubtypingForrestForClasses subtypingForrest = new SubtypingForrestForClasses(appView);
+ SubtypingForrestForClasses subtypingForrest = new SubtypingForrestForClasses(appView, classes);
// TODO(b/170078037): parallelize this code segment.
for (DexProgramClass root : subtypingForrest.getProgramRoots()) {
subtypingForrest.traverseNodeDepthFirst(
@@ -153,18 +153,18 @@
Map<Wrapper<DexMethod>, DexString> remappedClassVirtualMethods =
new HashMap<>(remappedVirtualMethods);
- Set<DexMethod> newDirectMethodReferences = new LinkedHashSet<>();
- Set<DexMethod> newVirtualMethodReferences = new LinkedHashSet<>();
-
+ Set<DexMethod> newVirtualMethodReferences = Sets.newIdentityHashSet();
clazz
.getMethodCollection()
- .replaceVirtualMethods(
+ .replaceAllVirtualMethods(
method ->
fixupVirtualMethod(
remappedClassVirtualMethods, newVirtualMethodReferences, method));
+
+ Set<DexMethod> newDirectMethodReferences = Sets.newIdentityHashSet();
clazz
.getMethodCollection()
- .replaceDirectMethods(method -> fixupDirectMethod(newDirectMethodReferences, method));
+ .replaceAllDirectMethods(method -> fixupDirectMethod(newDirectMethodReferences, method));
fixupFields(clazz.staticFields(), clazz::setStaticField);
fixupFields(clazz.instanceFields(), clazz::setInstanceField);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
index ec5cb59..14488b6 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -90,6 +90,10 @@
}
}
+ public int getArity() {
+ return methods.iterator().next().getReference().getArity();
+ }
+
private DexMethod moveMethod(ProgramMethod oldMethod) {
DexMethod oldMethodReference = oldMethod.getReference();
DexMethod method =
@@ -171,6 +175,13 @@
DexMethod templateReference = methods.iterator().next().getReference();
DexMethod originalMethodReference =
appView.graphLens().getOriginalMethodSignature(templateReference);
+ DexMethod bridgeMethodReference =
+ dexItemFactory.createFreshMethodName(
+ originalMethodReference.getName().toSourceString() + "$bridge",
+ null,
+ originalMethodReference.proto,
+ originalMethodReference.getHolderType(),
+ tryMethod -> target.lookupMethod(tryMethod) == null);
DexMethod newMethodReference =
dexItemFactory.createMethod(target.type, templateReference.proto, templateReference.name);
@@ -180,7 +191,7 @@
classIdField,
superMethod,
newMethodReference,
- originalMethodReference);
+ bridgeMethodReference);
DexEncodedMethod newMethod =
new DexEncodedMethod(
newMethodReference,
@@ -196,7 +207,7 @@
for (ProgramMethod oldMethod : methods) {
lensBuilder.moveMethod(oldMethod.getReference(), newMethodReference);
}
- lensBuilder.recordExtraOriginalSignature(originalMethodReference, newMethodReference);
+ lensBuilder.recordExtraOriginalSignature(bridgeMethodReference, newMethodReference);
target.addVirtualMethod(newMethod);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/AllInstantiatedOrUninstantiated.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/AllInstantiatedOrUninstantiated.java
new file mode 100644
index 0000000..75d61d9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/AllInstantiatedOrUninstantiated.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2020, 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.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
+public class AllInstantiatedOrUninstantiated extends MultiClassSameReferencePolicy<Boolean> {
+
+ private final AppView<AppInfoWithLiveness> appView;
+
+ public AllInstantiatedOrUninstantiated(AppView<AppInfoWithLiveness> appView) {
+ this.appView = appView;
+ }
+
+ @Override
+ public Boolean getMergeKey(DexProgramClass clazz) {
+ return appView.appInfo().isInstantiatedDirectlyOrIndirectly(clazz);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeIntoLessVisible.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeIntoLessVisible.java
index e579310..4e9b69e 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeIntoLessVisible.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeIntoLessVisible.java
@@ -14,7 +14,7 @@
public class DontMergeIntoLessVisible extends MultiClassPolicy {
@Override
- public Collection<Collection<DexProgramClass>> apply(Collection<DexProgramClass> group) {
+ public Collection<List<DexProgramClass>> apply(List<DexProgramClass> group) {
Iterator<DexProgramClass> iterator = group.iterator();
while (iterator.hasNext()) {
DexProgramClass clazz = iterator.next();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeSynchronizedClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeSynchronizedClasses.java
index 958e660..d8bebf9 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeSynchronizedClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeSynchronizedClasses.java
@@ -12,6 +12,7 @@
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
+import java.util.List;
public class DontMergeSynchronizedClasses extends MultiClassPolicy {
private final AppView<AppInfoWithLiveness> appView;
@@ -25,14 +26,14 @@
}
@Override
- public Collection<Collection<DexProgramClass>> apply(Collection<DexProgramClass> group) {
+ public Collection<List<DexProgramClass>> apply(List<DexProgramClass> group) {
// Gather all synchronized classes.
- Collection<Collection<DexProgramClass>> synchronizedGroups = new LinkedList<>();
+ Collection<List<DexProgramClass>> synchronizedGroups = new LinkedList<>();
group.removeIf(
clazz -> {
boolean synchronizationClass = isSynchronizationClass(clazz);
if (synchronizationClass) {
- Collection<DexProgramClass> synchronizedGroup = new LinkedList<>();
+ List<DexProgramClass> synchronizedGroup = new LinkedList<>();
synchronizedGroup.add(clazz);
synchronizedGroups.add(synchronizedGroup);
}
@@ -43,7 +44,7 @@
return Collections.singletonList(group);
}
- Iterator<Collection<DexProgramClass>> synchronizedGroupIterator = synchronizedGroups.iterator();
+ Iterator<List<DexProgramClass>> synchronizedGroupIterator = synchronizedGroups.iterator();
for (DexProgramClass clazz : group) {
if (!synchronizedGroupIterator.hasNext()) {
synchronizedGroupIterator = synchronizedGroups.iterator();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKeepRules.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKeepRules.java
index 969f43a..4d90a1e 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKeepRules.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKeepRules.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.horizontalclassmerging.policies;
-
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexMember;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoOverlappingConstructors.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoOverlappingConstructors.java
index 112580c..474fcea 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoOverlappingConstructors.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoOverlappingConstructors.java
@@ -42,7 +42,7 @@
}
@Override
- public Collection<Collection<DexProgramClass>> apply(Collection<DexProgramClass> group) {
+ public Collection<List<DexProgramClass>> apply(List<DexProgramClass> group) {
Map<DexProto, Set<DexProgramClass>> overlappingConstructors = new IdentityHashMap<>();
for (DexProgramClass clazz : group) {
@@ -106,12 +106,10 @@
}
// Map to collection
- Collection<Collection<DexProgramClass>> newGroups = new ArrayList<>();
+ Collection<List<DexProgramClass>> newGroups = new ArrayList<>();
for (Set<DexProgramClass> newGroup : groups) {
- List<DexProgramClass> newGroupList = new ArrayList<>(newGroup);
newGroups.add(new ArrayList<>(newGroup));
}
-
return newGroups;
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventChangingVisibility.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventChangingVisibility.java
index 3c317eb..8b35a5c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventChangingVisibility.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventChangingVisibility.java
@@ -23,10 +23,10 @@
public PreventChangingVisibility() {}
public static class TargetGroup {
- private final Collection<DexProgramClass> group = new LinkedList<>();
+ private final List<DexProgramClass> group = new LinkedList<>();
private final Map<Wrapper<DexMethod>, MethodAccessFlags> methodMap = new HashMap<>();
- public Collection<DexProgramClass> getGroup() {
+ public List<DexProgramClass> getGroup() {
return group;
}
@@ -53,7 +53,7 @@
}
@Override
- public Collection<Collection<DexProgramClass>> apply(Collection<DexProgramClass> group) {
+ public Collection<List<DexProgramClass>> apply(List<DexProgramClass> group) {
List<TargetGroup> groups = new ArrayList<>();
for (DexProgramClass clazz : group) {
@@ -66,7 +66,7 @@
}
}
- Collection<Collection<DexProgramClass>> newGroups = new ArrayList<>();
+ Collection<List<DexProgramClass>> newGroups = new ArrayList<>();
for (TargetGroup newGroup : groups) {
if (!isTrivial(newGroup.getGroup())) {
newGroups.add(newGroup.getGroup());
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoMainDex.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoMainDex.java
index f17e923..f8f0a3f 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoMainDex.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoMainDex.java
@@ -30,7 +30,7 @@
}
@Override
- public Collection<Collection<DexProgramClass>> apply(Collection<DexProgramClass> group) {
+ public Collection<List<DexProgramClass>> apply(List<DexProgramClass> group) {
List<DexProgramClass> mainDexMembers = new LinkedList<>();
Iterator<DexProgramClass> iterator = group.iterator();
while (iterator.hasNext()) {
@@ -41,14 +41,13 @@
}
}
- Collection<Collection<DexProgramClass>> newGroups = new LinkedList<>();
+ Collection<List<DexProgramClass>> newGroups = new LinkedList<>();
if (!isTrivial(mainDexMembers)) {
newGroups.add(mainDexMembers);
}
if (!isTrivial(group)) {
newGroups.add(group);
}
-
return newGroups;
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
index 4b112b8..bd59a0e 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
@@ -16,6 +16,7 @@
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
public class RespectPackageBoundaries extends MultiClassPolicy {
@@ -58,7 +59,7 @@
/** Sort unrestricted classes into restricted classes if they are in the same package. */
void tryFindRestrictedPackage(
LinkedList<DexProgramClass> unrestrictedClasses,
- Map<String, Collection<DexProgramClass>> restrictedClasses) {
+ Map<String, List<DexProgramClass>> restrictedClasses) {
Iterator<DexProgramClass> i = unrestrictedClasses.iterator();
while (i.hasNext()) {
DexProgramClass clazz = i.next();
@@ -72,8 +73,8 @@
}
@Override
- public Collection<Collection<DexProgramClass>> apply(Collection<DexProgramClass> group) {
- Map<String, Collection<DexProgramClass>> restrictedClasses = new LinkedHashMap<>();
+ public Collection<List<DexProgramClass>> apply(List<DexProgramClass> group) {
+ Map<String, List<DexProgramClass>> restrictedClasses = new LinkedHashMap<>();
LinkedList<DexProgramClass> unrestrictedClasses = new LinkedList<>();
// Sort all restricted classes into packages.
@@ -92,7 +93,7 @@
// TODO(b/166577694): Add the unrestricted classes to restricted groups, but ensure they aren't
// the merge target.
- Collection<Collection<DexProgramClass>> groups = new ArrayList<>(restrictedClasses.size() + 1);
+ Collection<List<DexProgramClass>> groups = new ArrayList<>(restrictedClasses.size() + 1);
if (unrestrictedClasses.size() > 1) {
groups.add(unrestrictedClasses);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
index 416f064..3ee0de7 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.errors.Unreachable;
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.DexDefinition;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -323,10 +324,11 @@
return false;
}
if (appView.appInfo().hasLiveness()) {
- DexEncodedMethod singleTarget =
+ DexClassAndMethod singleTarget =
instruction.lookupSingleTarget(appView.withLiveness(), context);
if (singleTarget != null) {
- return isTypeInitializedBy(instruction, type, singleTarget, appView, mode);
+ return isTypeInitializedBy(
+ instruction, type, singleTarget.getDefinition(), appView, mode);
}
}
DexMethod method = instruction.getInvokedMethod();
@@ -350,8 +352,9 @@
// Class initialization may fail with ExceptionInInitializerError.
return false;
}
- DexEncodedMethod method = instruction.lookupSingleTarget(appView, context);
- return method != null && isTypeInitializedBy(instruction, type, method, appView, mode);
+ DexClassAndMethod method = instruction.lookupSingleTarget(appView, context);
+ return method != null
+ && isTypeInitializedBy(instruction, type, method.getDefinition(), appView, mode);
}
public static boolean forInvokeSuper(
@@ -374,10 +377,11 @@
return false;
}
if (appView.appInfo().hasLiveness()) {
- DexEncodedMethod singleTarget =
+ DexClassAndMethod singleTarget =
instruction.lookupSingleTarget(appView.withLiveness(), context);
if (singleTarget != null) {
- return isTypeInitializedBy(instruction, type, singleTarget, appView, mode);
+ return isTypeInitializedBy(
+ instruction, type, singleTarget.getDefinition(), appView, mode);
}
}
DexMethod method = instruction.getInvokedMethod();
@@ -418,10 +422,11 @@
return false;
}
if (appView.appInfo().hasLiveness()) {
- DexEncodedMethod singleTarget =
+ DexClassAndMethod singleTarget =
instruction.lookupSingleTarget(appView.withLiveness(), context);
if (singleTarget != null) {
- return isTypeInitializedBy(instruction, type, singleTarget, appView, mode);
+ return isTypeInitializedBy(
+ instruction, type, singleTarget.getDefinition(), appView, mode);
}
}
DexMethod method = instruction.getInvokedMethod();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java
index 32c1bb1..c2a5b53 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.ir.analysis;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -36,9 +36,10 @@
return false;
}
if (instr.isInvokeMethod()) {
- DexEncodedMethod target =
+ DexClassAndMethod target =
instr.asInvokeMethod().lookupSingleTarget(appView, code.context());
- if (target != null && target.getOptimizationInfo().returnValueOnlyDependsOnArguments()) {
+ if (target != null
+ && target.getDefinition().getOptimizationInfo().returnValueOnlyDependsOnArguments()) {
continue;
}
return false;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
index b73a496..01ea16b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
@@ -6,8 +6,8 @@
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.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
@@ -132,13 +132,16 @@
InvokeMethod invoke = instruction.asInvokeMethod();
DexMethod method = invoke.getInvokedMethod();
if (method.holder.isClassType()) {
- DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, context);
+ DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context);
if (singleTarget != null) {
- markInitializedOnNormalExit(singleTarget.holder());
+ markInitializedOnNormalExit(singleTarget.getHolderType());
markInitializedOnNormalExit(
- singleTarget.getOptimizationInfo().getInitializedClassesOnNormalExit());
+ singleTarget
+ .getDefinition()
+ .getOptimizationInfo()
+ .getInitializedClassesOnNormalExit());
} else {
- markInitializedOnNormalExit(method.holder);
+ markInitializedOnNormalExit(method.getHolderType());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/equivalence/BasicBlockBehavioralSubsumption.java b/src/main/java/com/android/tools/r8/ir/analysis/equivalence/BasicBlockBehavioralSubsumption.java
index ea4040c..2fb3ef7 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/equivalence/BasicBlockBehavioralSubsumption.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/equivalence/BasicBlockBehavioralSubsumption.java
@@ -8,7 +8,7 @@
import static com.google.common.base.Predicates.or;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.ConstClass;
@@ -160,9 +160,10 @@
}
// For constructor calls include field initialization side effects.
if (instruction.isInvokeConstructor(appView.dexItemFactory())) {
- DexEncodedMethod singleTarget =
+ DexClassAndMethod singleTarget =
instruction.asInvokeDirect().lookupSingleTarget(appView, context);
- return singleTarget != null && !singleTarget.getOptimizationInfo().mayHaveSideEffects();
+ return singleTarget != null
+ && !singleTarget.getDefinition().getOptimizationInfo().mayHaveSideEffects();
}
return true;
}
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 3bc0ebe..d0fd119 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
@@ -7,6 +7,7 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -150,7 +151,7 @@
return;
}
- DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, context);
+ DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context);
if (singleTarget == null) {
// We just lost track.
abstractInstanceFieldValues.remove(clazz);
@@ -158,7 +159,11 @@
}
InstanceFieldInitializationInfoCollection initializationInfoCollection =
- singleTarget.getOptimizationInfo().getInstanceInitializerInfo().fieldInitializationInfos();
+ singleTarget
+ .getDefinition()
+ .getOptimizationInfo()
+ .getInstanceInitializerInfo()
+ .fieldInitializationInfos();
// Synchronize on the lattice element (abstractInstanceFieldValuesForClass) in case we process
// another allocation site of `clazz` concurrently.
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 f508479..d42d5d1 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
@@ -8,8 +8,8 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
@@ -43,14 +43,14 @@
private final InstanceFieldInitializationInfoFactory factory;
- private final DexEncodedMethod parentConstructor;
+ private final DexClassAndMethod parentConstructor;
private final InvokeDirect parentConstructorCall;
private InstanceFieldValueAnalysis(
AppView<AppInfoWithLiveness> appView,
IRCode code,
OptimizationFeedback feedback,
- DexEncodedMethod parentConstructor,
+ DexClassAndMethod parentConstructor,
InvokeDirect parentConstructorCall) {
super(appView, code, feedback);
this.factory = appView.instanceFieldInitializationInfoFactory();
@@ -90,7 +90,7 @@
return EmptyInstanceFieldInitializationInfoCollection.getInstance();
}
- DexEncodedMethod parentConstructor =
+ DexClassAndMethod parentConstructor =
parentConstructorCall.lookupSingleTarget(appView, code.context());
if (parentConstructor == null) {
return EmptyInstanceFieldInitializationInfoCollection.getInstance();
@@ -167,6 +167,7 @@
}
InstanceFieldInitializationInfoCollection infos =
parentConstructor
+ .getDefinition()
.getOptimizationInfo()
.getInstanceInitializerInfo()
.fieldInitializationInfos();
@@ -271,7 +272,7 @@
if (field.isFinal()) {
return true;
}
- if (appView.appInfo().isFieldOnlyWrittenInMethod(field, parentConstructor)) {
+ if (appView.appInfo().isFieldOnlyWrittenInMethod(field, parentConstructor.getDefinition())) {
return true;
}
// Otherwise, conservatively return false.
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 ebcdfff..1b4e723 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
@@ -9,8 +9,8 @@
import static com.android.tools.r8.ir.code.Opcodes.STATIC_PUT;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
@@ -339,13 +339,17 @@
return ObjectState.empty();
}
- DexEncodedMethod singleTarget = uniqueConstructorInvoke.lookupSingleTarget(appView, context);
+ DexClassAndMethod singleTarget = uniqueConstructorInvoke.lookupSingleTarget(appView, context);
if (singleTarget == null) {
return ObjectState.empty();
}
InstanceFieldInitializationInfoCollection initializationInfos =
- singleTarget.getOptimizationInfo().getInstanceInitializerInfo().fieldInitializationInfos();
+ singleTarget
+ .getDefinition()
+ .getOptimizationInfo()
+ .getInstanceInitializerInfo()
+ .fieldInitializationInfos();
if (initializationInfos.isEmpty()) {
return ObjectState.empty();
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/modeling/LibraryMethodReadSetModeling.java b/src/main/java/com/android/tools/r8/ir/analysis/modeling/LibraryMethodReadSetModeling.java
index 34ab168..f9a2e25 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/modeling/LibraryMethodReadSetModeling.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/modeling/LibraryMethodReadSetModeling.java
@@ -4,41 +4,39 @@
package com.android.tools.r8.ir.analysis.modeling;
-import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
import com.android.tools.r8.ir.code.InvokeMethod;
-import java.util.function.Predicate;
/** Models if a given library method may cause a program field to be read. */
public class LibraryMethodReadSetModeling {
public static AbstractFieldSet getModeledReadSetOrUnknown(
- InvokeMethod invoke, DexItemFactory dexItemFactory) {
+ AppView<?> appView, InvokeMethod invoke) {
DexMethod invokedMethod = invoke.getInvokedMethod();
// Check if it is a library method that does not have side effects. In that case it is safe to
// assume that the method does not read any fields, since even if it did, it would not be able
// to do anything with the values it read (since we will remove such invocations without side
// effects).
- Predicate<InvokeMethod> noSideEffectsPredicate =
- dexItemFactory.libraryMethodsWithoutSideEffects.get(invokedMethod);
- if (noSideEffectsPredicate != null && noSideEffectsPredicate.test(invoke)) {
+ if (appView
+ .getLibraryMethodSideEffectModelCollection()
+ .isCallToSideEffectFreeFinalMethod(invoke)) {
return EmptyFieldSet.getInstance();
}
// Already handled above.
- assert !dexItemFactory.classMethods.isReflectiveNameLookup(invokedMethod);
+ assert !appView.dexItemFactory().classMethods.isReflectiveNameLookup(invokedMethod);
// Modeling of other library methods.
DexType holder = invokedMethod.holder;
- if (holder == dexItemFactory.objectType) {
- if (invokedMethod == dexItemFactory.objectMembers.constructor) {
- return EmptyFieldSet.getInstance();
- }
+ if (holder == appView.dexItemFactory().objectType
+ && invokedMethod == appView.dexItemFactory().objectMembers.constructor) {
+ return EmptyFieldSet.getInstance();
}
return UnknownFieldSet.getInstance();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index 2a81647..c61ca0a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -3,10 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import static com.android.tools.r8.graph.DexEncodedMethod.asDexClassAndMethodOrNull;
+
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.code.InvokeDirectRange;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
@@ -125,22 +128,24 @@
}
@Override
- public DexEncodedMethod lookupSingleTarget(
+ public DexClassAndMethod lookupSingleTarget(
AppView<?> appView,
ProgramMethod context,
TypeElement receiverUpperBoundType,
ClassTypeElement receiverLowerBoundType) {
DexMethod invokedMethod = getInvokedMethod();
+ DexEncodedMethod result;
if (appView.appInfo().hasLiveness()) {
AppInfoWithLiveness appInfo = appView.appInfo().withLiveness();
- DexEncodedMethod result = appInfo.lookupDirectTarget(invokedMethod, context);
+ result = appInfo.lookupDirectTarget(invokedMethod, context);
assert verifyD8LookupResult(
result, appView.appInfo().lookupDirectTargetOnItself(invokedMethod, context));
- return result;
+ } else {
+ // In D8, we can treat invoke-direct instructions as having a single target if the invoke is
+ // targeting a method in the enclosing class.
+ result = appView.appInfo().lookupDirectTargetOnItself(invokedMethod, context);
}
- // In D8, we can treat invoke-direct instructions as having a single target if the invoke is
- // targeting a method in the enclosing class.
- return appView.appInfo().lookupDirectTargetOnItself(invokedMethod, context);
+ return asDexClassAndMethodOrNull(result, appView);
}
@Override
@@ -189,15 +194,19 @@
// Trivial instance initializers do not read any fields.
if (appView.dexItemFactory().isConstructor(invokedMethod)) {
- DexEncodedMethod singleTarget = lookupSingleTarget(appView, context);
+ DexClassAndMethod singleTarget = lookupSingleTarget(appView, context);
// If we have a single target in the program, then use the computed initializer info.
// If we have a single target in the library, then fallthrough to the library modeling below.
- if (singleTarget != null && singleTarget.isProgramMethod(appView)) {
- return singleTarget.getOptimizationInfo().getInstanceInitializerInfo().readSet();
+ if (singleTarget != null && singleTarget.isProgramMethod()) {
+ return singleTarget
+ .getDefinition()
+ .getOptimizationInfo()
+ .getInstanceInitializerInfo()
+ .readSet();
}
}
- return LibraryMethodReadSetModeling.getModeledReadSetOrUnknown(this, appView.dexItemFactory());
+ return LibraryMethodReadSetModeling.getModeledReadSetOrUnknown(appView, this);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
index 83acc1d..6412e17 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -3,11 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import static com.android.tools.r8.graph.DexEncodedMethod.asDexClassAndMethodOrNull;
import static com.android.tools.r8.ir.analysis.type.TypeAnalysis.toRefinedReceiverType;
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.code.InvokeInterfaceRange;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
@@ -95,25 +97,27 @@
}
@Override
- public DexEncodedMethod lookupSingleTarget(
+ public DexClassAndMethod lookupSingleTarget(
AppView<?> appView,
ProgramMethod context,
TypeElement receiverUpperBoundType,
ClassTypeElement receiverLowerBoundType) {
- if (appView.appInfo().hasLiveness()) {
- AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
- return appViewWithLiveness
- .appInfo()
- .lookupSingleVirtualTarget(
- getInvokedMethod(),
- context,
- true,
- appView,
- toRefinedReceiverType(
- receiverUpperBoundType, getInvokedMethod(), appViewWithLiveness),
- receiverLowerBoundType);
+ if (!appView.appInfo().hasLiveness()) {
+ return null;
}
- return null;
+ AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+ DexEncodedMethod result =
+ appViewWithLiveness
+ .appInfo()
+ .lookupSingleVirtualTarget(
+ getInvokedMethod(),
+ context,
+ true,
+ appView,
+ toRefinedReceiverType(
+ receiverUpperBoundType, getInvokedMethod(), appViewWithLiveness),
+ receiverLowerBoundType);
+ return asDexClassAndMethodOrNull(result, appView);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index c8dd6ec..6daa9a4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.cf.TypeVerificationHelper;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -75,11 +76,10 @@
// In subclasses, e.g., invoke-virtual or invoke-super, use a narrower receiver type by using
// receiver type and calling context---the holder of the method where the current invocation is.
// TODO(b/140204899): Refactor lookup methods to be defined in a single place.
- public abstract DexEncodedMethod lookupSingleTarget(AppView<?> appView, ProgramMethod context);
+ public abstract DexClassAndMethod lookupSingleTarget(AppView<?> appView, ProgramMethod context);
public final ProgramMethod lookupSingleProgramTarget(AppView<?> appView, ProgramMethod context) {
- DexEncodedMethod singleTarget = lookupSingleTarget(appView, context);
- return singleTarget != null ? singleTarget.asProgramMethod(appView) : null;
+ return DexClassAndMethod.asProgramMethodOrNull(lookupSingleTarget(appView, context));
}
// TODO(b/140204899): Refactor lookup methods to be defined in a single place.
@@ -202,16 +202,16 @@
@Override
public AbstractFieldSet readSet(AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
- return LibraryMethodReadSetModeling.getModeledReadSetOrUnknown(this, appView.dexItemFactory());
+ return LibraryMethodReadSetModeling.getModeledReadSetOrUnknown(appView, this);
}
@Override
public AbstractValue getAbstractValue(
AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
assert hasOutValue();
- DexEncodedMethod method = lookupSingleTarget(appView, context);
+ DexClassAndMethod method = lookupSingleTarget(appView, context);
if (method != null) {
- return method.getOptimizationInfo().getAbstractReturnValue();
+ return method.getDefinition().getOptimizationInfo().getAbstractReturnValue();
}
return UnknownValue.getInstance();
}
@@ -227,9 +227,10 @@
@Override
public boolean throwsNpeIfValueIsNull(Value value, AppView<?> appView, ProgramMethod context) {
- DexEncodedMethod singleTarget = lookupSingleTarget(appView, context);
+ DexClassAndMethod singleTarget = lookupSingleTarget(appView, context);
if (singleTarget != null) {
- BitSet nonNullParamOrThrow = singleTarget.getOptimizationInfo().getNonNullParamOrThrow();
+ BitSet nonNullParamOrThrow =
+ singleTarget.getDefinition().getOptimizationInfo().getNonNullParamOrThrow();
if (nonNullParamOrThrow != null) {
int argumentIndex = inValues.indexOf(value);
return argumentIndex >= 0 && nonNullParamOrThrow.get(argumentIndex);
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
index b006d6a..2aeb9b5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
@@ -3,10 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
-import static com.android.tools.r8.graph.DexEncodedMethod.asProgramMethodOrNull;
-
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;
import com.android.tools.r8.graph.DexProgramClass;
@@ -26,7 +25,6 @@
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.List;
-import java.util.function.Predicate;
public abstract class InvokeMethodWithReceiver extends InvokeMethod {
@@ -73,7 +71,7 @@
}
@Override
- public final DexEncodedMethod lookupSingleTarget(AppView<?> appView, ProgramMethod context) {
+ public final DexClassAndMethod lookupSingleTarget(AppView<?> appView, ProgramMethod context) {
TypeElement receiverUpperBoundType = null;
ClassTypeElement receiverLowerBoundType = null;
if (appView.enableWholeProgramOptimizations()) {
@@ -84,7 +82,7 @@
return lookupSingleTarget(appView, context, receiverUpperBoundType, receiverLowerBoundType);
}
- public abstract DexEncodedMethod lookupSingleTarget(
+ public abstract DexClassAndMethod lookupSingleTarget(
AppView<?> appView,
ProgramMethod context,
TypeElement receiverUpperBoundType,
@@ -95,9 +93,8 @@
ProgramMethod context,
TypeElement receiverUpperBoundType,
ClassTypeElement receiverLowerBoundType) {
- return asProgramMethodOrNull(
- lookupSingleTarget(appView, context, receiverUpperBoundType, receiverLowerBoundType),
- appView);
+ return DexClassAndMethod.asProgramMethodOrNull(
+ lookupSingleTarget(appView, context, receiverUpperBoundType, receiverLowerBoundType));
}
@Override
@@ -197,9 +194,9 @@
}
// Check if it is a call to one of library methods that are known to be side-effect free.
- Predicate<InvokeMethod> noSideEffectsPredicate =
- appView.dexItemFactory().libraryMethodsWithoutSideEffects.get(getInvokedMethod());
- if (noSideEffectsPredicate != null && noSideEffectsPredicate.test(this)) {
+ if (appView
+ .getLibraryMethodSideEffectModelCollection()
+ .isCallToSideEffectFreeFinalMethod(this)) {
return false;
}
@@ -237,18 +234,26 @@
}
// Find the target and check if the invoke may have side effects.
- DexEncodedMethod target = lookupSingleTarget(appViewWithLiveness, context);
- if (target == null) {
+ DexClassAndMethod singleTarget = lookupSingleTarget(appViewWithLiveness, context);
+ if (singleTarget == null) {
return true;
}
- // Verify that the target method does not have side-effects.
- if (appViewWithLiveness.appInfo().noSideEffects.containsKey(target.method)) {
+ if (singleTarget.isLibraryMethod()
+ && appView
+ .getLibraryMethodSideEffectModelCollection()
+ .isSideEffectFree(this, singleTarget.asLibraryMethod())) {
return false;
}
- MethodOptimizationInfo optimizationInfo = target.getOptimizationInfo();
- if (target.isInstanceInitializer()) {
+ // Verify that the target method does not have side-effects.
+ if (appViewWithLiveness.appInfo().noSideEffects.containsKey(singleTarget.getReference())) {
+ return false;
+ }
+
+ DexEncodedMethod singleTargetDefinition = singleTarget.getDefinition();
+ MethodOptimizationInfo optimizationInfo = singleTargetDefinition.getOptimizationInfo();
+ if (singleTargetDefinition.isInstanceInitializer()) {
InstanceInitializerInfo initializerInfo = optimizationInfo.getInstanceInitializerInfo();
if (!initializerInfo.mayHaveOtherSideEffectsThanInstanceFieldAssignments()) {
return !isInvokeDirect();
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
index dbcafdc..425262b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
@@ -7,7 +7,7 @@
import com.android.tools.r8.code.InvokePolymorphicRange;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProto;
@@ -125,7 +125,7 @@
}
@Override
- public DexEncodedMethod lookupSingleTarget(AppView<?> appView, ProgramMethod context) {
+ public DexClassAndMethod lookupSingleTarget(AppView<?> appView, ProgramMethod context) {
// TODO(herhut): Implement lookup target for invokePolymorphic.
return null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index 696d674..bf3a599 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -3,10 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import static com.android.tools.r8.graph.DexEncodedMethod.asDexClassAndMethodOrNull;
+
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.code.InvokeStaticRange;
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;
import com.android.tools.r8.graph.DexType;
@@ -26,7 +29,6 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.Sets;
import java.util.List;
-import java.util.function.Predicate;
public class InvokeStatic extends InvokeMethod {
@@ -107,24 +109,27 @@
}
@Override
- public DexEncodedMethod lookupSingleTarget(AppView<?> appView, ProgramMethod context) {
+ public DexClassAndMethod lookupSingleTarget(AppView<?> appView, ProgramMethod context) {
DexMethod invokedMethod = getInvokedMethod();
+ DexEncodedMethod result;
if (appView.appInfo().hasLiveness()) {
AppInfoWithLiveness appInfo = appView.appInfo().withLiveness();
- DexEncodedMethod result = appInfo.lookupStaticTarget(invokedMethod, context);
+ result = appInfo.lookupStaticTarget(invokedMethod, context);
assert verifyD8LookupResult(
result, appView.appInfo().lookupStaticTargetOnItself(invokedMethod, context));
- return result;
+ } else {
+ // Allow optimizing static library invokes in D8.
+ DexClass clazz = appView.definitionForHolder(getInvokedMethod());
+ if (clazz != null
+ && (clazz.isLibraryClass() || appView.libraryMethodOptimizer().isModeled(clazz.type))) {
+ result = clazz.lookupMethod(getInvokedMethod());
+ } else {
+ // In D8, we can treat invoke-static instructions as having a single target if the invoke is
+ // targeting a method in the enclosing class.
+ result = appView.appInfo().lookupStaticTargetOnItself(invokedMethod, context);
+ }
}
- // Allow optimizing static library invokes in D8.
- DexClass clazz = appView.definitionForHolder(getInvokedMethod());
- if (clazz != null
- && (clazz.isLibraryClass() || appView.libraryMethodOptimizer().isModeled(clazz.type))) {
- return clazz.lookupMethod(getInvokedMethod());
- }
- // In D8, we can treat invoke-static instructions as having a single target if the invoke is
- // targeting a method in the enclosing class.
- return appView.appInfo().lookupStaticTargetOnItself(invokedMethod, context);
+ return asDexClassAndMethodOrNull(result, appView);
}
@Override
@@ -173,9 +178,9 @@
}
// Check if it is a call to one of library methods that are known to be side-effect free.
- Predicate<InvokeMethod> noSideEffectsPredicate =
- appView.dexItemFactory().libraryMethodsWithoutSideEffects.get(getInvokedMethod());
- if (noSideEffectsPredicate != null && noSideEffectsPredicate.test(this)) {
+ if (appView
+ .getLibraryMethodSideEffectModelCollection()
+ .isCallToSideEffectFreeFinalMethod(this)) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
index 34fa59c..8ce0b70 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
@@ -3,9 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import static com.android.tools.r8.graph.DexEncodedMethod.asDexClassAndMethodOrNull;
+
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.code.InvokeSuperRange;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
@@ -102,7 +105,7 @@
}
@Override
- public DexEncodedMethod lookupSingleTarget(
+ public DexClassAndMethod lookupSingleTarget(
AppView<?> appView,
ProgramMethod context,
TypeElement receiverUpperBoundType,
@@ -111,7 +114,8 @@
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
AppInfoWithLiveness appInfo = appViewWithLiveness.appInfo();
if (appInfo.isSubtype(context.getHolderType(), getInvokedMethod().holder)) {
- return appInfo.lookupSuperTarget(getInvokedMethod(), context);
+ DexEncodedMethod result = appInfo.lookupSuperTarget(getInvokedMethod(), context);
+ return asDexClassAndMethodOrNull(result, appView);
}
}
return null;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index e7fb163..d4f737e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -3,12 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import static com.android.tools.r8.graph.DexEncodedMethod.asDexClassAndMethodOrNull;
import static com.android.tools.r8.ir.analysis.type.TypeAnalysis.toRefinedReceiverType;
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.code.InvokeVirtualRange;
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;
import com.android.tools.r8.graph.DexType;
@@ -96,7 +98,7 @@
}
@Override
- public DexEncodedMethod lookupSingleTarget(
+ public DexClassAndMethod lookupSingleTarget(
AppView<?> appView,
ProgramMethod context,
TypeElement receiverUpperBoundType,
@@ -105,38 +107,43 @@
appView, context, receiverUpperBoundType, receiverLowerBoundType, getInvokedMethod());
}
- public static DexEncodedMethod lookupSingleTarget(
+ public static DexClassAndMethod lookupSingleTarget(
AppView<?> appView,
ProgramMethod context,
TypeElement receiverUpperBoundType,
ClassTypeElement receiverLowerBoundType,
DexMethod method) {
+ DexEncodedMethod result = null;
if (appView.appInfo().hasLiveness()) {
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
- return appViewWithLiveness
- .appInfo()
- .lookupSingleVirtualTarget(
- method,
- context,
- false,
- appView,
- toRefinedReceiverType(receiverUpperBoundType, method, appViewWithLiveness),
- receiverLowerBoundType);
- }
- // In D8, allow lookupSingleTarget() to be used for finding final library methods. This is used
- // for library modeling.
- DexType holder = method.holder;
- if (holder.isClassType()) {
- DexClass clazz = appView.definitionFor(holder);
- if (clazz != null
- && (clazz.isLibraryClass() || appView.libraryMethodOptimizer().isModeled(clazz.type))) {
- DexEncodedMethod singleTargetCandidate = clazz.lookupMethod(method);
- if (singleTargetCandidate != null && (clazz.isFinal() || singleTargetCandidate.isFinal())) {
- return singleTargetCandidate;
+ result =
+ appViewWithLiveness
+ .appInfo()
+ .lookupSingleVirtualTarget(
+ method,
+ context,
+ false,
+ appView,
+ toRefinedReceiverType(receiverUpperBoundType, method, appViewWithLiveness),
+ receiverLowerBoundType);
+ } else {
+ // In D8, allow lookupSingleTarget() to be used for finding final library methods. This is
+ // used
+ // for library modeling.
+ DexType holder = method.holder;
+ if (holder.isClassType()) {
+ DexClass clazz = appView.definitionFor(holder);
+ if (clazz != null
+ && (clazz.isLibraryClass() || appView.libraryMethodOptimizer().isModeled(clazz.type))) {
+ DexEncodedMethod singleTargetCandidate = clazz.lookupMethod(method);
+ if (singleTargetCandidate != null
+ && (clazz.isFinal() || singleTargetCandidate.isFinal())) {
+ result = singleTargetCandidate;
+ }
}
}
}
- return null;
+ return asDexClassAndMethodOrNull(result, appView);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
index c337afe..9356caa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
@@ -186,6 +186,10 @@
.containsKey(method.getHolderType())) {
return false;
}
+ if (!appView.options().desugaredLibraryConfiguration.supportAllCallbacksFromLibrary
+ && appView.options().isDesugaredLibraryCompilation()) {
+ return false;
+ }
return overridesLibraryMethod(method);
}
@@ -212,6 +216,9 @@
if (!dexClass.isLibraryClass() && !appView.options().isDesugaredLibraryCompilation()) {
continue;
}
+ if (!shouldGenerateCallbacksForEmulateInterfaceAPIs(dexClass)) {
+ continue;
+ }
DexEncodedMethod dexEncodedMethod = dexClass.lookupVirtualMethod(method.getReference());
if (dexEncodedMethod != null) {
// In this case, the object will be wrapped.
@@ -224,6 +231,16 @@
return foundOverrideToRewrite;
}
+ private boolean shouldGenerateCallbacksForEmulateInterfaceAPIs(DexClass dexClass) {
+ if (appView.options().desugaredLibraryConfiguration.supportAllCallbacksFromLibrary) {
+ return true;
+ }
+ Map<DexType, DexType> emulateLibraryInterfaces =
+ appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface();
+ return !(emulateLibraryInterfaces.containsKey(dexClass.type)
+ || emulateLibraryInterfaces.containsValue(dexClass.type));
+ }
+
private synchronized void registerCallback(ProgramMethod method) {
// In R8 we should be in the enqueuer, therefore we can duplicate a default method and both
// methods will be desugared.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
index 542c5d7..9896987 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
@@ -36,12 +36,15 @@
public class DesugaredLibraryConfiguration {
public static final String FALL_BACK_SYNTHESIZED_CLASSES_PACKAGE_PREFIX = "j$/";
+ public static final boolean FALL_BACK_SUPPORT_ALL_CALLBACKS_FROM_LIBRARY = true;
+
public static final DesugaredLibraryConfiguration EMPTY_DESUGARED_LIBRARY_CONFIGURATION =
new DesugaredLibraryConfiguration(
AndroidApiLevel.B,
false,
FALL_BACK_SYNTHESIZED_CLASSES_PACKAGE_PREFIX,
null,
+ FALL_BACK_SUPPORT_ALL_CALLBACKS_FROM_LIBRARY,
ImmutableMap.of(),
ImmutableMap.of(),
ImmutableMap.of(),
@@ -51,11 +54,18 @@
ImmutableList.of(),
ImmutableList.of());
- // TODO(b/158632510): should use DexString, DexType, DexMethod or so on when possible.
private final AndroidApiLevel requiredCompilationAPILevel;
private final boolean libraryCompilation;
private final String synthesizedLibraryClassesPackagePrefix;
private final String identifier;
+ // Setting supportAllCallbacksFromLibrary reduces the number of generated call-backs,
+ // more specifically:
+ // - no call-back is generated for emulated interface method overrides (forEach, etc.)
+ // - no call-back is generated inside the desugared library itself.
+ // Such setting decreases significantly the desugared library dex file, but virtual calls from
+ // within the library to desugared library classes instances as receiver may be incorrect, for
+ // example the method forEach in Iterable may be executed over a concrete implementation.
+ public final boolean supportAllCallbacksFromLibrary;
private final Map<String, String> rewritePrefix;
private final Map<DexType, DexType> emulateLibraryInterface;
private final Map<DexString, Map<DexType, DexType>> retargetCoreLibMember;
@@ -76,6 +86,7 @@
true,
FALL_BACK_SYNTHESIZED_CLASSES_PACKAGE_PREFIX,
"testingOnlyVersion",
+ FALL_BACK_SUPPORT_ALL_CALLBACKS_FROM_LIBRARY,
prefix,
ImmutableMap.of(),
ImmutableMap.of(),
@@ -95,6 +106,7 @@
boolean libraryCompilation,
String packagePrefix,
String identifier,
+ boolean supportAllCallbacksFromLibrary,
Map<String, String> rewritePrefix,
Map<DexType, DexType> emulateLibraryInterface,
Map<DexString, Map<DexType, DexType>> retargetCoreLibMember,
@@ -107,6 +119,7 @@
this.libraryCompilation = libraryCompilation;
this.synthesizedLibraryClassesPackagePrefix = packagePrefix;
this.identifier = identifier;
+ this.supportAllCallbacksFromLibrary = supportAllCallbacksFromLibrary;
this.rewritePrefix = rewritePrefix;
this.emulateLibraryInterface = emulateLibraryInterface;
this.retargetCoreLibMember = retargetCoreLibMember;
@@ -204,6 +217,7 @@
private Set<DexType> wrapperConversions = Sets.newIdentityHashSet();
private List<Pair<DexType, DexString>> dontRewriteInvocation = new ArrayList<>();
private List<String> extraKeepRules = Collections.emptyList();
+ private boolean supportAllCallbacksFromLibrary = FALL_BACK_SUPPORT_ALL_CALLBACKS_FROM_LIBRARY;
private Builder(DexItemFactory dexItemFactory, Reporter reporter, Origin origin) {
this.factory = dexItemFactory;
@@ -344,6 +358,10 @@
return factory.createType(DescriptorUtils.javaTypeToDescriptor(stringClass));
}
+ public void setSupportAllCallbacksFromLibrary(boolean supportAllCallbacksFromLibrary) {
+ this.supportAllCallbacksFromLibrary = supportAllCallbacksFromLibrary;
+ }
+
public DesugaredLibraryConfiguration build() {
validate();
return new DesugaredLibraryConfiguration(
@@ -351,6 +369,7 @@
libraryCompilation,
synthesizedLibraryClassesPackagePrefix,
identifier,
+ supportAllCallbacksFromLibrary,
ImmutableMap.copyOf(rewritePrefix),
ImmutableMap.copyOf(emulateLibraryInterface),
ImmutableMap.copyOf(retargetCoreLibMember),
@@ -373,5 +392,6 @@
origin));
}
}
+
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
index 7586210..a20c8e0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
@@ -47,6 +47,7 @@
static final String DONT_REWRITE_KEY = "dont_rewrite";
static final String BACKPORT_KEY = "backport";
static final String SHRINKER_CONFIG_KEY = "shrinker_config";
+ static final String SUPPORT_ALL_CALLBACKS_FROM_LIBRARY_KEY = "support_all_callbacks_from_library";
private final DexItemFactory dexItemFactory;
private final Reporter reporter;
@@ -149,6 +150,12 @@
}
configurationBuilder.setExtraKeepRules(extraKeepRules);
}
+
+ if (jsonConfig.has(SUPPORT_ALL_CALLBACKS_FROM_LIBRARY_KEY)) {
+ boolean supportAllCallbacksFromLibrary =
+ jsonConfig.get(SUPPORT_ALL_CALLBACKS_FROM_LIBRARY_KEY).getAsBoolean();
+ configurationBuilder.setSupportAllCallbacksFromLibrary(supportAllCallbacksFromLibrary);
+ }
configurationAmender.accept(configurationBuilder);
DesugaredLibraryConfiguration config = configurationBuilder.build();
configurationBuilder = null;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 4a3ee89..f9985d5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -48,6 +48,7 @@
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IterableUtils;
+import com.android.tools.r8.utils.ObjectUtils;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
@@ -1004,18 +1005,19 @@
GenericSignature.ClassSignature classSignature = clazz.getClassSignature();
for (int i = 0; i < clazz.interfaces.size(); i++) {
DexType itf = clazz.interfaces.values[i];
- assert emulatedInterfaces.containsKey(itf);
- List<GenericSignature.FieldTypeSignature> typeArguments;
- if (classSignature == null) {
- typeArguments = Collections.emptyList();
- } else {
- GenericSignature.ClassTypeSignature classTypeSignature =
- classSignature.superInterfaceSignatures().get(i);
- assert itf == classTypeSignature.type();
- typeArguments = classTypeSignature.typeArguments();
+ if (emulatedInterfaces.containsKey(itf)) {
+ List<GenericSignature.FieldTypeSignature> typeArguments;
+ if (classSignature == null) {
+ typeArguments = Collections.emptyList();
+ } else {
+ GenericSignature.ClassTypeSignature classTypeSignature =
+ classSignature.superInterfaceSignatures().get(i);
+ assert itf == classTypeSignature.type();
+ typeArguments = classTypeSignature.typeArguments();
+ }
+ newInterfaces.add(
+ new GenericSignature.ClassTypeSignature(emulatedInterfaces.get(itf), typeArguments));
}
- newInterfaces.add(
- new GenericSignature.ClassTypeSignature(emulatedInterfaces.get(itf), typeArguments));
}
clazz.replaceInterfaces(newInterfaces);
}
@@ -1118,6 +1120,10 @@
|| missing.isD8R8SynthesizedClassType()
|| isCompanionClassType(missing)
|| emulatedInterfaces.containsValue(missing)
+ || ObjectUtils.getBooleanOrElse(
+ options.getProguardConfiguration(),
+ configuration -> configuration.getDontWarnPatterns().matches(missing),
+ false)
|| options.desugaredLibraryConfiguration.getCustomConversions().containsValue(missing);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 9b8f930..c9e47c2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -233,7 +233,7 @@
new DexEncodedMethod(
mainMethod,
MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC | Constants.ACC_FINAL | Constants.ACC_SYNTHETIC, false),
+ Constants.ACC_PUBLIC | Constants.ACC_FINAL, false),
MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
index 0008896..d6eae9a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
@@ -8,8 +8,8 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -225,13 +225,13 @@
private boolean computeAssumedValuesFromSingleTarget(
IRCode code, InvokeMethod invoke, AssumedValues.Builder assumedValuesBuilder) {
- DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.context());
+ DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, code.context());
if (singleTarget == null) {
return false;
}
boolean needsAssumeInstruction = false;
- MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
+ MethodOptimizationInfo optimizationInfo = singleTarget.getDefinition().getOptimizationInfo();
// Case (2), invocations that are guaranteed to return a non-null value.
Value outValue = invoke.outValue();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 4c61a1c..61b13cb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
@@ -1253,9 +1254,14 @@
}
// Check if the invoked method is known to return one of its arguments.
- DexEncodedMethod target = invoke.lookupSingleTarget(appView, code.context());
- if (target != null && target.getOptimizationInfo().returnsArgument()) {
- int argumentIndex = target.getOptimizationInfo().getReturnedArgument();
+ DexClassAndMethod target = invoke.lookupSingleTarget(appView, code.context());
+ if (target == null) {
+ continue;
+ }
+
+ MethodOptimizationInfo optimizationInfo = target.getDefinition().getOptimizationInfo();
+ if (optimizationInfo.returnsArgument()) {
+ int argumentIndex = optimizationInfo.getReturnedArgument();
// Replace the out value of the invoke with the argument and ignore the out value.
if (argumentIndex >= 0 && checkArgumentType(invoke, argumentIndex)) {
Value argument = invoke.arguments().get(argumentIndex);
@@ -2878,13 +2884,14 @@
}
InvokeMethod invoke = instruction.asInvokeMethod();
- DexEncodedMethod singleTarget =
+ DexClassAndMethod singleTarget =
invoke.lookupSingleTarget(appView.withLiveness(), code.context());
if (singleTarget == null) {
continue;
}
- MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
+ MethodOptimizationInfo optimizationInfo =
+ singleTarget.getDefinition().getOptimizationInfo();
// If the invoke instruction is a null check, we can remove it.
boolean isNullCheck = false;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
index 36fba35..912a030 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
@@ -11,6 +11,9 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
@@ -147,13 +150,19 @@
continue;
}
SingleFieldValue singleFieldValue = abstractValue.asSingleFieldValue();
+ DexType fieldHolderType = singleFieldValue.getField().getHolderType();
if (context.getDefinition().isClassInitializer()
- && context.getHolderType() == singleFieldValue.getField().holder) {
+ && context.getHolderType() == fieldHolderType) {
// Avoid that canonicalization inserts a read before the unique write in the class
// initializer, as that would change the program behavior.
continue;
}
- if (current.instructionMayHaveSideEffects(appView, context)) {
+ DexClass fieldHolder = appView.definitionFor(fieldHolderType);
+ DexEncodedField field = singleFieldValue.getField().lookupOnClass(fieldHolder);
+ if (field == null
+ || !field.isEnum()
+ || current.instructionMayHaveSideEffects(appView, context)) {
+ // Only allow canonicalization of enums.
continue;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
index 4c7570c..d648bc1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
@@ -123,19 +123,18 @@
// Check if the instruction can be rewritten to invoke-super. This allows inlining of the
// enclosing method into contexts outside the current class.
if (appView.options().testing.enableInvokeSuperToInvokeVirtualRewriting) {
- DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, context);
+ DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context);
if (singleTarget != null) {
- DexClass holder = appView.definitionForHolder(singleTarget, context);
- assert holder != null;
DexMethod invokedMethod = invoke.getInvokedMethod();
- DexEncodedMethod newSingleTarget =
+ DexClassAndMethod newSingleTarget =
InvokeVirtual.lookupSingleTarget(
appView,
context,
invoke.getReceiver().getDynamicUpperBoundType(appView),
invoke.getReceiver().getDynamicLowerBoundType(appView),
invokedMethod);
- if (newSingleTarget == singleTarget) {
+ if (newSingleTarget != null
+ && newSingleTarget.getReference() == singleTarget.getReference()) {
it.replaceCurrentInstruction(
new InvokeVirtual(invokedMethod, invoke.outValue(), invoke.arguments()));
continue;
@@ -173,12 +172,11 @@
continue;
}
InvokeInterface invoke = current.asInvokeInterface();
- DexEncodedMethod target = invoke.lookupSingleTarget(appView, context);
+ DexClassAndMethod target = invoke.lookupSingleTarget(appView, context);
if (target == null) {
continue;
}
- DexType holderType = target.holder();
- DexClass holderClass = appView.definitionFor(holderType);
+ DexClass holderClass = target.getHolder();
// Make sure we are not landing on another interface, e.g., interface's default method.
if (holderClass == null || holderClass.isInterface()) {
continue;
@@ -190,7 +188,7 @@
}
InvokeVirtual devirtualizedInvoke =
- new InvokeVirtual(target.method, invoke.outValue(), invoke.inValues());
+ new InvokeVirtual(target.getReference(), invoke.outValue(), invoke.inValues());
it.replaceCurrentInstruction(devirtualizedInvoke);
devirtualizedCall.put(invoke, devirtualizedInvoke);
@@ -204,11 +202,12 @@
// CodeRewriter#removeTrivialCheckCastAndInstanceOfInstructions}.
// a <- check-cast A i // Otherwise ART verification error.
// (out <-) invoke-virtual a, ... A#foo
- if (holderType != invoke.getInvokedMethod().holder) {
+ if (holderClass.getType() != invoke.getInvokedMethod().holder) {
Value receiver = invoke.getReceiver();
TypeElement receiverTypeLattice = receiver.getType();
TypeElement castTypeLattice =
- TypeElement.fromDexType(holderType, receiverTypeLattice.nullability(), appView);
+ TypeElement.fromDexType(
+ holderClass.getType(), receiverTypeLattice.nullability(), appView);
// Avoid adding trivial cast and up-cast.
// We should not use strictlyLessThan(castType, receiverType), which detects downcast,
// due to side-casts, e.g., A (unused) < I, B < I, and cast from A to B.
@@ -224,8 +223,8 @@
// a2 <- check-cast A i // We should be able to reuse a1 here!
// invoke-virtual a2, ... A#m2 (from I#m2)
if (castedReceiverCache.containsKey(receiver)
- && castedReceiverCache.get(receiver).containsKey(holderType)) {
- Value cachedReceiver = castedReceiverCache.get(receiver).get(holderType);
+ && castedReceiverCache.get(receiver).containsKey(holderClass.getType())) {
+ Value cachedReceiver = castedReceiverCache.get(receiver).get(holderClass.getType());
if (dominatorTree.dominatedBy(block, cachedReceiver.definition.getBlock())) {
newReceiver = cachedReceiver;
}
@@ -237,9 +236,9 @@
// Cache the new receiver with a narrower type to avoid redundant checkcast.
if (!receiver.hasLocalInfo()) {
castedReceiverCache.putIfAbsent(receiver, new IdentityHashMap<>());
- castedReceiverCache.get(receiver).put(holderType, newReceiver);
+ castedReceiverCache.get(receiver).put(holderClass.getType(), newReceiver);
}
- CheckCast checkCast = new CheckCast(newReceiver, receiver, holderType);
+ CheckCast checkCast = new CheckCast(newReceiver, receiver, holderClass.getType());
checkCast.setPosition(invoke.getPosition());
newCheckCastInstructions.add(checkCast);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
index 10d17e7..50443c6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.ir.optimize;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.ProgramMethod;
@@ -17,6 +17,7 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.StringUtils;
@@ -29,7 +30,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-import java.util.function.Predicate;
/**
* Canonicalize idempotent function calls.
@@ -154,10 +154,14 @@
// return the resolution result such that the call site can perform the accessibility
// check, or (iii) always perform the accessibility check such that it can be skipped
// at the call site.
- DexEncodedMethod target = invoke.lookupSingleTarget(appViewWithLiveness, context);
- if (target == null
- || target.getOptimizationInfo().mayHaveSideEffects()
- || !target.getOptimizationInfo().returnValueOnlyDependsOnArguments()) {
+ DexClassAndMethod target = invoke.lookupSingleTarget(appViewWithLiveness, context);
+ if (target == null) {
+ continue;
+ }
+
+ MethodOptimizationInfo optimizationInfo = target.getDefinition().getOptimizationInfo();
+ if (optimizationInfo.mayHaveSideEffects()
+ || !optimizationInfo.returnValueOnlyDependsOnArguments()) {
continue;
}
@@ -272,12 +276,10 @@
private boolean isIdempotentLibraryMethodInvoke(InvokeMethod invoke) {
DexMethod invokedMethod = invoke.getInvokedMethod();
- Predicate<InvokeMethod> noSideEffectPredicate =
- factory.libraryMethodsWithoutSideEffects.get(invokedMethod);
- if (noSideEffectPredicate == null || !noSideEffectPredicate.test(invoke)) {
- return false;
- }
- return factory.libraryMethodsWithReturnValueDependingOnlyOnArguments.contains(invokedMethod);
+ return appView
+ .getLibraryMethodSideEffectModelCollection()
+ .isCallToSideEffectFreeFinalMethod(invoke)
+ && factory.libraryMethodsWithReturnValueDependingOnlyOnArguments.contains(invokedMethod);
}
private static void insertCanonicalizedInvokeWithInValues(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 96fd58c..621f3b9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -8,6 +8,7 @@
import static com.google.common.base.Predicates.alwaysTrue;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexDefinition;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -103,12 +104,16 @@
|| appView.appInfo().noSideEffects.containsKey(field.field);
}
- private boolean mayPropagateValueFor(DexEncodedMethod method) {
- if (method.isProgramMethod(appView)) {
- return appView.appInfo().mayPropagateValueFor(method.method);
+ private boolean mayPropagateValueFor(DexClassAndMethod method) {
+ if (method.isProgramMethod()) {
+ return appView.appInfo().mayPropagateValueFor(method.getReference());
}
- return appView.appInfo().assumedValues.containsKey(method.method)
- || appView.appInfo().noSideEffects.containsKey(method.method);
+ return appView.appInfo().assumedValues.containsKey(method.getReference())
+ || appView.appInfo().noSideEffects.containsKey(method.getReference());
+ }
+
+ private ProguardMemberRuleLookup lookupMemberRule(DexClassAndMethod method) {
+ return method != null ? lookupMemberRule(method.getDefinition()) : null;
}
private ProguardMemberRuleLookup lookupMemberRule(DexDefinition definition) {
@@ -245,7 +250,7 @@
return;
}
- DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, context);
+ DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context);
ProguardMemberRuleLookup lookup = lookupMemberRule(singleTarget);
if (lookup == null) {
// -assumenosideeffects rules are applied to upward visible and overriding methods, but only
@@ -264,7 +269,7 @@
if (singleTarget != null
&& lookup.type == RuleType.ASSUME_NO_SIDE_EFFECTS
&& !lookup.rule.hasReturnValue()) {
- ProguardMemberRule rule = appView.appInfo().assumedValues.get(singleTarget.toReference());
+ ProguardMemberRule rule = appView.appInfo().assumedValues.get(singleTarget.getReference());
if (rule != null) {
lookup = new ProguardMemberRuleLookup(RuleType.ASSUME_VALUES, rule);
}
@@ -280,7 +285,8 @@
return;
}
- AbstractValue abstractReturnValue = singleTarget.getOptimizationInfo().getAbstractReturnValue();
+ AbstractValue abstractReturnValue =
+ singleTarget.getDefinition().getOptimizationInfo().getAbstractReturnValue();
if (abstractReturnValue.isSingleValue()) {
SingleValue singleReturnValue = abstractReturnValue.asSingleValue();
@@ -299,7 +305,7 @@
replaceInstructionByNullCheckIfPossible(invoke, iterator, context);
} else if (invoke.isInvokeStatic()) {
replaceInstructionByInitClassIfPossible(
- invoke, singleTarget.holder(), code, iterator, context);
+ invoke, singleTarget.getHolderType(), code, iterator, context);
}
// Insert the definition of the replacement.
@@ -309,7 +315,7 @@
} else {
iterator.add(replacement);
}
- singleTarget.getMutableOptimizationInfo().markAsPropagated();
+ singleTarget.getDefinition().getMutableOptimizationInfo().markAsPropagated();
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
index b6bd724..b2ab54f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
@@ -8,10 +8,11 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndField;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -148,19 +149,24 @@
}
}
- public boolean isFinal(DexEncodedField field) {
- if (field.isProgramField(appView)) {
- return field.isFinal();
+ public boolean isFinal(DexClassAndField field) {
+ if (field.isProgramField()) {
+ // Treat this field as being final if it is declared final or we have determined a constant
+ // value for it.
+ return field.getDefinition().isFinal()
+ || field.getDefinition().getOptimizationInfo().getAbstractValue().isSingleValue();
}
- return appView.libraryMethodOptimizer().isFinalLibraryField(field);
+ return appView.libraryMethodOptimizer().isFinalLibraryField(field.getDefinition());
}
- private DexEncodedField resolveField(DexField field) {
+ private DexClassAndField resolveField(DexField field) {
if (appView.enableWholeProgramOptimizations()) {
- return appView.appInfo().withLiveness().resolveField(field).getResolvedField();
+ SuccessfulFieldResolutionResult resolutionResult =
+ appView.appInfo().withLiveness().resolveField(field).asSuccessfulResolution();
+ return resolutionResult != null ? resolutionResult.getResolutionPair() : null;
}
- if (field.holder == method.getHolderType()) {
- return method.getHolder().lookupField(field);
+ if (field.getHolderType() == method.getHolderType()) {
+ return method.getHolder().lookupProgramField(field);
}
return null;
}
@@ -187,9 +193,9 @@
while (it.hasNext()) {
Instruction instruction = it.next();
if (instruction.isFieldInstruction()) {
- DexField field = instruction.asFieldInstruction().getField();
- DexEncodedField definition = resolveField(field);
- if (definition == null || definition.isVolatile()) {
+ DexField reference = instruction.asFieldInstruction().getField();
+ DexClassAndField field = resolveField(reference);
+ if (field == null || field.getDefinition().isVolatile()) {
killAllNonFinalActiveFields();
continue;
}
@@ -200,7 +206,7 @@
continue;
}
Value object = instanceGet.object().getAliasedValue();
- FieldAndObject fieldAndObject = new FieldAndObject(field, object);
+ FieldAndObject fieldAndObject = new FieldAndObject(reference, object);
FieldValue replacement = activeState.getInstanceFieldValue(fieldAndObject);
if (replacement != null) {
replacement.eliminateRedundantRead(it, instanceGet);
@@ -215,10 +221,11 @@
killNonFinalActiveFields(instancePut);
// ... but at least we know the field value for this particular object.
Value object = instancePut.object().getAliasedValue();
- FieldAndObject fieldAndObject = new FieldAndObject(field, object);
+ FieldAndObject fieldAndObject = new FieldAndObject(reference, object);
ExistingValue value = new ExistingValue(instancePut.value());
- if (isFinal(definition)) {
- assert method.getDefinition().isInstanceInitializer()
+ if (isFinal(field)) {
+ assert !field.getDefinition().isFinal()
+ || method.getDefinition().isInstanceInitializer()
|| verifyWasInstanceInitializer();
activeState.putFinalInstanceField(fieldAndObject, value);
} else {
@@ -229,7 +236,7 @@
if (staticGet.outValue().hasLocalInfo()) {
continue;
}
- FieldValue replacement = activeState.getStaticFieldValue(field);
+ FieldValue replacement = activeState.getStaticFieldValue(reference);
if (replacement != null) {
replacement.eliminateRedundantRead(it, staticGet);
} else {
@@ -237,10 +244,10 @@
// field values.
killNonFinalActiveFields(staticGet);
FieldValue value = new ExistingValue(staticGet.value());
- if (isFinal(definition)) {
- activeState.putFinalStaticField(field, value);
+ if (isFinal(field)) {
+ activeState.putFinalStaticField(reference, value);
} else {
- activeState.putNonFinalStaticField(field, value);
+ activeState.putNonFinalStaticField(reference, value);
}
}
} else if (instruction.isStaticPut()) {
@@ -249,11 +256,12 @@
// field values.
killNonFinalActiveFields(staticPut);
ExistingValue value = new ExistingValue(staticPut.value());
- if (definition.isFinal()) {
- assert method.getDefinition().isClassInitializer();
- activeState.putFinalStaticField(field, value);
+ if (isFinal(field)) {
+ assert !field.getDefinition().isFinal()
+ || method.getDefinition().isClassInitializer();
+ activeState.putFinalStaticField(reference, value);
} else {
- activeState.putNonFinalStaticField(field, value);
+ activeState.putNonFinalStaticField(reference, value);
}
}
} else if (instruction.isInitClass()) {
@@ -352,14 +360,14 @@
return;
}
- DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method);
- if (singleTarget == null || !singleTarget.isInstanceInitializer()) {
+ DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, method);
+ if (singleTarget == null || !singleTarget.getDefinition().isInstanceInitializer()) {
killAllNonFinalActiveFields();
return;
}
InstanceInitializerInfo instanceInitializerInfo =
- singleTarget.getOptimizationInfo().getInstanceInitializerInfo();
+ singleTarget.getDefinition().getOptimizationInfo().getInstanceInitializerInfo();
if (instanceInitializerInfo.mayHaveOtherSideEffectsThanInstanceFieldAssignments()) {
killAllNonFinalActiveFields();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index c8cc00e..6b0b61d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -6,18 +6,19 @@
import static com.android.tools.r8.graph.DexEncodedMethod.asProgramMethodOrNull;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-import static com.google.common.base.Predicates.alwaysFalse;
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AccessControl;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.LibraryMethod;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
@@ -296,21 +297,22 @@
}
// TODO(b/156853206): Avoid duplicating resolution.
- DexEncodedMethod singleTargetMethod = invokeMethod.lookupSingleTarget(appView, method);
- if (singleTargetMethod == null) {
+ DexClassAndMethod singleTarget = invokeMethod.lookupSingleTarget(appView, method);
+ if (singleTarget == null) {
return user; // Not eligible.
}
- if (isEligibleLibraryMethodCall(invokeMethod, singleTargetMethod)) {
+ if (singleTarget.isLibraryMethod()
+ && isEligibleLibraryMethodCall(invokeMethod, singleTarget.asLibraryMethod())) {
continue;
}
- ProgramMethod singleTarget = singleTargetMethod.asProgramMethod(appView);
- if (!isEligibleSingleTarget(singleTarget)) {
+ ProgramMethod singleProgramTarget = singleTarget.asProgramMethod();
+ if (!isEligibleSingleTarget(singleProgramTarget)) {
return user; // Not eligible.
}
- if (AccessControl.isClassAccessible(singleTarget.getHolder(), method, appView)
+ if (AccessControl.isClassAccessible(singleProgramTarget.getHolder(), method, appView)
.isPossiblyFalse()) {
return user; // Not eligible.
}
@@ -324,7 +326,7 @@
&& !invoke.inValues().isEmpty()
&& root.outValue() == invoke.getReceiver();
if (isCorrespondingConstructorCall) {
- InliningInfo inliningInfo = isEligibleConstructorCall(invoke, singleTarget);
+ InliningInfo inliningInfo = isEligibleConstructorCall(invoke, singleProgramTarget);
if (inliningInfo != null) {
methodCallsOnInstance.put(invoke, inliningInfo);
continue;
@@ -340,7 +342,7 @@
InvokeMethodWithReceiver invoke = user.asInvokeMethodWithReceiver();
InliningInfo inliningInfo =
isEligibleDirectVirtualMethodCall(
- invoke, resolutionResult, singleTarget, indirectUsers, defaultOracle);
+ invoke, resolutionResult, singleProgramTarget, indirectUsers, defaultOracle);
if (inliningInfo != null) {
methodCallsOnInstance.put(invoke, inliningInfo);
continue;
@@ -351,7 +353,7 @@
if (isExtraMethodCall(invokeMethod)) {
assert !invokeMethod.isInvokeSuper();
assert !invokeMethod.isInvokePolymorphic();
- if (isExtraMethodCallEligible(invokeMethod, singleTarget, defaultOracle)) {
+ if (isExtraMethodCallEligible(invokeMethod, singleProgramTarget, defaultOracle)) {
continue;
}
}
@@ -646,12 +648,13 @@
continue;
}
- DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, method);
- if (singleTarget != null) {
- Predicate<InvokeMethod> noSideEffectsPredicate =
- dexItemFactory.libraryMethodsWithoutSideEffects.getOrDefault(
- singleTarget.method, alwaysFalse());
- if (noSideEffectsPredicate.test(invoke)) {
+ DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, method);
+ if (singleTarget != null && singleTarget.isLibraryMethod()) {
+ boolean isSideEffectFree =
+ appView
+ .getLibraryMethodSideEffectModelCollection()
+ .isSideEffectFree(invoke, singleTarget.asLibraryMethod());
+ if (isSideEffectFree) {
if (!invoke.hasOutValue() || !invoke.outValue().hasAnyUsers()) {
removeInstruction(invoke);
continue;
@@ -1169,13 +1172,13 @@
return true;
}
- private boolean isEligibleLibraryMethodCall(InvokeMethod invoke, DexEncodedMethod singleTarget) {
- Predicate<InvokeMethod> noSideEffectsPredicate =
- dexItemFactory.libraryMethodsWithoutSideEffects.get(singleTarget.method);
- if (noSideEffectsPredicate != null && noSideEffectsPredicate.test(invoke)) {
+ private boolean isEligibleLibraryMethodCall(InvokeMethod invoke, LibraryMethod singleTarget) {
+ boolean isSideEffectFree =
+ appView.getLibraryMethodSideEffectModelCollection().isSideEffectFree(invoke, singleTarget);
+ if (isSideEffectFree) {
return !invoke.hasOutValue() || !invoke.outValue().hasAnyUsers();
}
- if (singleTarget.method == dexItemFactory.objectsMethods.requireNonNull) {
+ if (singleTarget.getReference() == dexItemFactory.objectsMethods.requireNonNull) {
return !invoke.hasOutValue() || !invoke.outValue().hasAnyUsers();
}
return false;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index c766569..334e4fa 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -211,7 +212,7 @@
DexMethod invokedMethod = invokeStatic.getInvokedMethod();
DexProgramClass enumClass = getEnumUnboxingCandidateOrNull(invokedMethod.holder);
if (enumClass != null) {
- DexEncodedMethod method = invokeStatic.lookupSingleTarget(appView, context);
+ DexClassAndMethod method = invokeStatic.lookupSingleTarget(appView, context);
if (method != null) {
eligibleEnums.add(enumClass.type);
} else {
@@ -726,19 +727,13 @@
}
return Reason.INVALID_INVOKE_ON_ARRAY;
}
- DexEncodedMethod encodedSingleTarget =
- invokeMethod.lookupSingleTarget(appView, code.context());
- if (encodedSingleTarget == null) {
+ DexClassAndMethod singleTarget = invokeMethod.lookupSingleTarget(appView, code.context());
+ if (singleTarget == null) {
return Reason.INVALID_INVOKE;
}
- DexMethod singleTarget = encodedSingleTarget.method;
- DexClass dexClass = appView.definitionFor(singleTarget.holder, code.context());
- if (dexClass == null) {
- assert false;
- return Reason.INVALID_INVOKE;
- }
+ DexClass dexClass = singleTarget.getHolder();
if (dexClass.isProgramClass()) {
- if (dexClass.isEnum() && encodedSingleTarget.isInstanceInitializer()) {
+ if (dexClass.isEnum() && singleTarget.getDefinition().isInstanceInitializer()) {
if (code.method().holder() == dexClass.type && code.method().isClassInitializer()) {
// The enum instance initializer is allowed to be called only from the enum clinit.
return Reason.ELIGIBLE;
@@ -748,10 +743,10 @@
}
// Check that the enum-value only flows into parameters whose type exactly matches the
// enum's type.
- int offset = BooleanUtils.intValue(!encodedSingleTarget.isStatic());
- for (int i = 0; i < singleTarget.proto.parameters.size(); i++) {
+ int offset = BooleanUtils.intValue(!singleTarget.getDefinition().isStatic());
+ for (int i = 0; i < singleTarget.getReference().getParameters().size(); i++) {
if (invokeMethod.getArgument(offset + i) == enumValue) {
- if (singleTarget.proto.parameters.values[i].toBaseType(factory) != enumClass.type) {
+ if (singleTarget.getReference().getParameter(i).toBaseType(factory) != enumClass.type) {
return Reason.GENERIC_INVOKE;
}
}
@@ -768,43 +763,44 @@
return Reason.INVALID_INVOKE;
}
assert dexClass.isLibraryClass();
+ DexMethod singleTargetReference = singleTarget.getReference();
if (dexClass.type != factory.enumType) {
// System.identityHashCode(Object) is supported for proto enums.
// Object#getClass without outValue and Objects.requireNonNull are supported since R8
// rewrites explicit null checks to such instructions.
- if (singleTarget == factory.javaLangSystemMethods.identityHashCode) {
+ if (singleTargetReference == factory.javaLangSystemMethods.identityHashCode) {
return Reason.ELIGIBLE;
}
- if (singleTarget == factory.stringMembers.valueOf) {
+ if (singleTargetReference == factory.stringMembers.valueOf) {
addRequiredNameData(enumClass.type);
return Reason.ELIGIBLE;
}
- if (singleTarget == factory.objectMembers.getClass
+ if (singleTargetReference == factory.objectMembers.getClass
&& (!invokeMethod.hasOutValue() || !invokeMethod.outValue().hasAnyUsers())) {
// This is a hidden null check.
return Reason.ELIGIBLE;
}
- if (singleTarget == factory.objectsMethods.requireNonNull
- || singleTarget == factory.objectsMethods.requireNonNullWithMessage) {
+ if (singleTargetReference == factory.objectsMethods.requireNonNull
+ || singleTargetReference == factory.objectsMethods.requireNonNullWithMessage) {
return Reason.ELIGIBLE;
}
return Reason.UNSUPPORTED_LIBRARY_CALL;
}
// TODO(b/147860220): EnumSet and EnumMap may be interesting to model.
- if (singleTarget == factory.enumMembers.compareTo) {
+ if (singleTargetReference == factory.enumMembers.compareTo) {
return Reason.ELIGIBLE;
- } else if (singleTarget == factory.enumMembers.equals) {
+ } else if (singleTargetReference == factory.enumMembers.equals) {
return Reason.ELIGIBLE;
- } else if (singleTarget == factory.enumMembers.nameMethod
- || singleTarget == factory.enumMembers.toString) {
+ } else if (singleTargetReference == factory.enumMembers.nameMethod
+ || singleTargetReference == factory.enumMembers.toString) {
assert invokeMethod.asInvokeMethodWithReceiver().getReceiver() == enumValue;
addRequiredNameData(enumClass.type);
return Reason.ELIGIBLE;
- } else if (singleTarget == factory.enumMembers.ordinalMethod) {
+ } else if (singleTargetReference == factory.enumMembers.ordinalMethod) {
return Reason.ELIGIBLE;
- } else if (singleTarget == factory.enumMembers.hashCode) {
+ } else if (singleTargetReference == factory.enumMembers.hashCode) {
return Reason.ELIGIBLE;
- } else if (singleTarget == factory.enumMembers.constructor) {
+ } else if (singleTargetReference == factory.enumMembers.constructor) {
// Enum constructor call is allowed only if called from an enum initializer.
if (code.method().isInstanceInitializer() && code.method().holder() == enumClass.type) {
return Reason.ELIGIBLE;
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 9125b1c..cb6a6fa 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
@@ -45,6 +45,7 @@
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.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
@@ -260,11 +261,11 @@
case INVOKE_STATIC:
{
InvokeStatic invoke = insn.asInvokeStatic();
- DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, context);
+ DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context);
if (singleTarget == null) {
return; // Not allowed.
}
- if (singleTarget.method == dexItemFactory.objectsMethods.requireNonNull) {
+ if (singleTarget.getReference() == dexItemFactory.objectsMethods.requireNonNull) {
if (!invoke.hasOutValue() || !invoke.outValue().hasAnyUsers()) {
continue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java
index 92635f2..1da512d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.Argument;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.CheckCast;
import com.android.tools.r8.ir.code.ConstClass;
@@ -65,6 +66,8 @@
boolean isValidInitClass(CodeProcessor context, DexType clazz);
+ boolean isValidHolder(CodeProcessor context, DexType holder);
+
void patch(ApplyStrategy context, NewInstance newInstance);
void patch(ApplyStrategy context, InvokeMethod invoke);
@@ -74,6 +77,8 @@
void patch(ApplyStrategy context, StaticGet staticGet);
void patch(ApplyStrategy context, InitClass initClass);
+
+ void patch(ApplyStrategy context, Argument argument);
}
// No-op strategy.
@@ -120,6 +125,11 @@
}
@Override
+ public boolean isValidHolder(CodeProcessor context, DexType holder) {
+ return false;
+ }
+
+ @Override
public void patch(ApplyStrategy context, NewInstance newInstance) {
throw new Unreachable();
}
@@ -143,6 +153,11 @@
public void patch(ApplyStrategy context, InitClass initClass) {
throw new Unreachable();
}
+
+ @Override
+ public void patch(ApplyStrategy context, Argument argument) {
+ throw new Unreachable();
+ }
};
public final AppView<AppInfoWithLiveness> appView;
@@ -383,6 +398,20 @@
return null;
}
+ @Override
+ public Void visit(Argument instruction) {
+ if (instruction.outValue() != code.getThis()) {
+ return null;
+ }
+ Strategy strategy = strategyProvider.apply(method.getHolderType());
+ if (strategy.isValidHolder(this, method.getHolderType())) {
+ if (shouldRewrite(method.getHolderType())) {
+ process(strategy, instruction);
+ }
+ }
+ return null;
+ }
+
abstract void process(Strategy strategy, InvokeMethod invokeMethod);
abstract void process(Strategy strategy, NewInstance newInstance);
@@ -396,4 +425,6 @@
abstract void process(Strategy strategy, StaticGet staticGet);
abstract void process(Strategy strategy, InitClass initClass);
+
+ abstract void process(Strategy strategy, Argument argument);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index 9ccbbbc..8aacbde 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
import com.android.tools.r8.ir.analysis.type.DestructivePhiTypeUpdater;
+import com.android.tools.r8.ir.code.Argument;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InitClass;
import com.android.tools.r8.ir.code.InstanceGet;
@@ -542,6 +543,11 @@
void process(Strategy strategy, InitClass initClass) {
queueForProcessing(method);
}
+
+ @Override
+ void process(Strategy strategy, Argument argument) {
+ throw new Unreachable();
+ }
}
public final class ApplyStrategy extends CodeProcessor {
@@ -661,6 +667,11 @@
void process(Strategy strategy, InitClass initClass) {
strategy.patch(this, initClass);
}
+
+ @Override
+ void process(Strategy strategy, Argument argument) {
+ strategy.patch(this, argument);
+ }
}
private final class LambdaMergerOptimizationInfoFixer
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
index 112b37c..2c88700 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.code.Argument;
import com.android.tools.r8.ir.code.CheckCast;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.InitClass;
@@ -120,6 +121,12 @@
}
@Override
+ public boolean isValidHolder(CodeProcessor context, DexType holder) {
+ assert group.containsLambda(holder);
+ return true;
+ }
+
+ @Override
public void patch(ApplyStrategy context, NewInstance newInstance) {
DexType oldType = newInstance.clazz;
DexType newType = group.getGroupClassType();
@@ -217,6 +224,17 @@
context.instructions().replaceCurrentInstruction(pachedInitClass);
}
+ @Override
+ public void patch(ApplyStrategy context, Argument argument) {
+ // An argument can be a direct operand to a phi that we potentially could not remove.
+ assert argument.getIndex() == 0;
+ // The argument value will be replaced by the invoke value.
+ argument
+ .outValue()
+ .setType(TypeElement.fromDexType(group.getGroupClassType(), maybeNull(), context.appView));
+ context.recordTypeHasChanged(argument.outValue());
+ }
+
private void patchInitializer(CodeProcessor context, InvokeDirect invoke) {
// Patching includes:
// - change of methods
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java
index dcb128a..69bdef8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.ir.optimize.library;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
@@ -39,13 +39,13 @@
IRCode code,
InstructionListIterator instructionIterator,
InvokeMethod invoke,
- DexEncodedMethod singleTarget,
+ DexClassAndMethod singleTarget,
Set<Value> affectedValues) {
- if (singleTarget.method == dexItemFactory.booleanMembers.booleanValue) {
+ if (singleTarget.getReference() == dexItemFactory.booleanMembers.booleanValue) {
optimizeBooleanValue(code, instructionIterator, invoke);
- } else if (singleTarget.method == dexItemFactory.booleanMembers.parseBoolean) {
+ } else if (singleTarget.getReference() == dexItemFactory.booleanMembers.parseBoolean) {
optimizeParseBoolean(code, instructionIterator, invoke);
- } else if (singleTarget.method == dexItemFactory.booleanMembers.valueOf) {
+ } else if (singleTarget.getReference() == dexItemFactory.booleanMembers.valueOf) {
optimizeValueOf(code, instructionIterator, invoke, affectedValues);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java
index 658fcb5..4323220 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java
@@ -7,7 +7,7 @@
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -36,9 +36,9 @@
IRCode code,
InstructionListIterator instructionIterator,
InvokeMethod invoke,
- DexEncodedMethod singleTarget,
+ DexClassAndMethod singleTarget,
Set<Value> affectedValues) {
- if (singleTarget.method == appView.dexItemFactory().enumMembers.valueOf
+ if (singleTarget.getReference() == appView.dexItemFactory().enumMembers.valueOf
&& invoke.inValues().get(0).isConstClass()) {
insertAssumeDynamicType(code, instructionIterator, invoke);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
index bcea380..d6c8ada 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
@@ -5,8 +5,8 @@
package com.android.tools.r8.ir.optimize.library;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory.LibraryMembers;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
@@ -118,7 +118,7 @@
Instruction instruction = instructionIterator.next();
if (instruction.isInvokeMethod()) {
InvokeMethod invoke = instruction.asInvokeMethod();
- DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.context());
+ DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, code.context());
if (singleTarget != null) {
optimizeInvoke(code, instructionIterator, invoke, singleTarget, affectedValues);
}
@@ -133,11 +133,11 @@
IRCode code,
InstructionListIterator instructionIterator,
InvokeMethod invoke,
- DexEncodedMethod singleTarget,
+ DexClassAndMethod singleTarget,
Set<Value> affectedValues) {
LibraryMethodModelCollection optimizer =
libraryMethodModelCollections.getOrDefault(
- singleTarget.holder(), NopLibraryMethodModelCollection.getInstance());
+ singleTarget.getHolderType(), NopLibraryMethodModelCollection.getInstance());
optimizer.optimize(code, instructionIterator, invoke, singleTarget, affectedValues);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java
index 5608b94..843e9ab 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.ir.optimize.library;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -29,6 +29,6 @@
IRCode code,
InstructionListIterator instructionIterator,
InvokeMethod invoke,
- DexEncodedMethod singleTarget,
+ DexClassAndMethod singleTarget,
Set<Value> affectedValues);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java
new file mode 100644
index 0000000..55d1dc3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java
@@ -0,0 +1,82 @@
+// Copyright (c) 2020, 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.library;
+
+import static com.google.common.base.Predicates.alwaysFalse;
+import static com.google.common.base.Predicates.alwaysTrue;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.LibraryMethod;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Predicate;
+
+public class LibraryMethodSideEffectModelCollection {
+
+ private final Map<DexMethod, Predicate<InvokeMethod>> finalMethodsWithoutSideEffects;
+ private final Set<DexMethod> nonFinalMethodsWithoutSideEffects;
+
+ public LibraryMethodSideEffectModelCollection(DexItemFactory dexItemFactory) {
+ finalMethodsWithoutSideEffects = buildFinalMethodsWithoutSideEffects(dexItemFactory);
+ nonFinalMethodsWithoutSideEffects = buildNonFinalMethodsWithoutSideEffects(dexItemFactory);
+ }
+
+ private static Map<DexMethod, Predicate<InvokeMethod>> buildFinalMethodsWithoutSideEffects(
+ DexItemFactory dexItemFactory) {
+ ImmutableMap.Builder<DexMethod, Predicate<InvokeMethod>> builder =
+ ImmutableMap.<DexMethod, Predicate<InvokeMethod>>builder()
+ .put(dexItemFactory.enumMembers.constructor, alwaysTrue())
+ .put(dexItemFactory.npeMethods.init, alwaysTrue())
+ .put(dexItemFactory.npeMethods.initWithMessage, alwaysTrue())
+ .put(dexItemFactory.objectMembers.constructor, alwaysTrue())
+ .put(dexItemFactory.objectMembers.getClass, alwaysTrue())
+ .put(dexItemFactory.stringMembers.hashCode, alwaysTrue());
+ putAll(builder, dexItemFactory.classMethods.getNames, alwaysTrue());
+ putAll(
+ builder,
+ dexItemFactory.stringBufferMethods.constructorMethods,
+ dexItemFactory.stringBufferMethods::constructorInvokeIsSideEffectFree);
+ putAll(
+ builder,
+ dexItemFactory.stringBuilderMethods.constructorMethods,
+ dexItemFactory.stringBuilderMethods::constructorInvokeIsSideEffectFree);
+ putAll(builder, dexItemFactory.boxedValueOfMethods(), alwaysTrue());
+ return builder.build();
+ }
+
+ private static Set<DexMethod> buildNonFinalMethodsWithoutSideEffects(
+ DexItemFactory dexItemFactory) {
+ return ImmutableSet.of(
+ dexItemFactory.objectMembers.equals,
+ dexItemFactory.objectMembers.hashCode,
+ dexItemFactory.objectMembers.toString);
+ }
+
+ private static void putAll(
+ ImmutableMap.Builder<DexMethod, Predicate<InvokeMethod>> builder,
+ Iterable<DexMethod> methods,
+ Predicate<InvokeMethod> predicate) {
+ for (DexMethod method : methods) {
+ builder.put(method, predicate);
+ }
+ }
+
+ public boolean isCallToSideEffectFreeFinalMethod(InvokeMethod invoke) {
+ return finalMethodsWithoutSideEffects
+ .getOrDefault(invoke.getInvokedMethod(), alwaysFalse())
+ .test(invoke);
+ }
+
+ // This intentionally takes the invoke instruction since the determination of whether a library
+ // method has side effects may depend on the arguments.
+ public boolean isSideEffectFree(InvokeMethod invoke, LibraryMethod singleTarget) {
+ return isCallToSideEffectFreeFinalMethod(invoke)
+ || nonFinalMethodsWithoutSideEffects.contains(singleTarget.getReference());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LogMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LogMethodOptimizer.java
index 35220c7..f8847d3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LogMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LogMethodOptimizer.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.ir.optimize.library;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
@@ -104,11 +104,11 @@
IRCode code,
InstructionListIterator instructionIterator,
InvokeMethod invoke,
- DexEncodedMethod singleTarget,
+ DexClassAndMethod singleTarget,
Set<Value> affectedValues) {
int maxRemovedAndroidLogLevel =
appView.options().getProguardConfiguration().getMaxRemovedAndroidLogLevel();
- if (singleTarget.method == isLoggableMethod) {
+ if (singleTarget.getReference() == isLoggableMethod) {
Value logLevelValue = invoke.arguments().get(1).getAliasedValue();
if (!logLevelValue.isPhi() && !logLevelValue.hasLocalInfo()) {
Instruction definition = logLevelValue.definition;
@@ -118,27 +118,27 @@
code, instructionIterator, invoke, maxRemovedAndroidLogLevel >= logLevel ? 0 : 1);
}
}
- } else if (singleTarget.method == vMethod) {
+ } else if (singleTarget.getReference() == vMethod) {
if (maxRemovedAndroidLogLevel >= VERBOSE) {
replaceInvokeWithConstNumber(code, instructionIterator, invoke, 0);
}
- } else if (singleTarget.method == dMethod) {
+ } else if (singleTarget.getReference() == dMethod) {
if (maxRemovedAndroidLogLevel >= DEBUG) {
replaceInvokeWithConstNumber(code, instructionIterator, invoke, 0);
}
- } else if (singleTarget.method == iMethod) {
+ } else if (singleTarget.getReference() == iMethod) {
if (maxRemovedAndroidLogLevel >= INFO) {
replaceInvokeWithConstNumber(code, instructionIterator, invoke, 0);
}
- } else if (singleTarget.method == wMethod) {
+ } else if (singleTarget.getReference() == wMethod) {
if (maxRemovedAndroidLogLevel >= WARN) {
replaceInvokeWithConstNumber(code, instructionIterator, invoke, 0);
}
- } else if (singleTarget.method == eMethod) {
+ } else if (singleTarget.getReference() == eMethod) {
if (maxRemovedAndroidLogLevel >= ERROR) {
replaceInvokeWithConstNumber(code, instructionIterator, invoke, 0);
}
- } else if (singleTarget.method == wtfMethod) {
+ } else if (singleTarget.getReference() == wtfMethod) {
if (maxRemovedAndroidLogLevel >= ASSERT) {
replaceInvokeWithConstNumber(code, instructionIterator, invoke, 0);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/NopLibraryMethodModelCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/NopLibraryMethodModelCollection.java
index 9a0980f..f852393 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/NopLibraryMethodModelCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/NopLibraryMethodModelCollection.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.ir.optimize.library;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -34,6 +34,6 @@
IRCode code,
InstructionListIterator instructionIterator,
InvokeMethod invoke,
- DexEncodedMethod singleTarget,
+ DexClassAndMethod singleTarget,
Set<Value> affectedValues) {}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectMethodOptimizer.java
index 1674baf..f16af45 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectMethodOptimizer.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.ir.optimize.library;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.IRCode;
@@ -32,9 +32,9 @@
IRCode code,
InstructionListIterator instructionIterator,
InvokeMethod invoke,
- DexEncodedMethod singleTarget,
+ DexClassAndMethod singleTarget,
Set<Value> affectedValues) {
- if (singleTarget.method == dexItemFactory.objectMembers.getClass) {
+ if (singleTarget.getReference() == dexItemFactory.objectMembers.getClass) {
optimizeGetClass(instructionIterator, invoke);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java
index c78a123..8fcc1ac 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.ir.optimize.library;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.IRCode;
@@ -32,9 +32,9 @@
IRCode code,
InstructionListIterator instructionIterator,
InvokeMethod invoke,
- DexEncodedMethod singleTarget,
+ DexClassAndMethod singleTarget,
Set<Value> affectedValues) {
- if (dexItemFactory.objectsMethods.isRequireNonNullMethod(singleTarget.method)) {
+ if (dexItemFactory.objectsMethods.isRequireNonNullMethod(singleTarget.getReference())) {
optimizeRequireNonNull(instructionIterator, invoke, affectedValues);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
index 64dce88..18ad3e6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.ir.optimize.library;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
@@ -38,9 +38,9 @@
IRCode code,
InstructionListIterator instructionIterator,
InvokeMethod invoke,
- DexEncodedMethod singleTarget,
+ DexClassAndMethod singleTarget,
Set<Value> affectedValues) {
- if (singleTarget.method == dexItemFactory.stringMembers.equals) {
+ if (singleTarget.getReference() == dexItemFactory.stringMembers.equals) {
optimizeEquals(code, instructionIterator, invoke);
}
}
@@ -74,9 +74,10 @@
return false;
}
- DexEncodedMethod singleTarget =
+ DexClassAndMethod singleTarget =
classNameDefinition.asInvokeVirtual().lookupSingleTarget(appView, context);
- if (singleTarget == null || singleTarget.method != dexItemFactory.classMethods.getName) {
+ if (singleTarget == null
+ || singleTarget.getReference() != dexItemFactory.classMethods.getName) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
index 7e66078..9b12469 100644
--- a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
@@ -60,6 +60,11 @@
}
@Override
+ public Iterable<DexType> getOriginalTypes(DexType type) {
+ return getPrevious().getOriginalTypes(type);
+ }
+
+ @Override
public DexField getOriginalFieldSignature(DexField field) {
return getPrevious().getOriginalFieldSignature(field);
}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
index 3f625f2..02b56e3 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
@@ -84,6 +84,11 @@
}
@Override
+ public Iterable<DexType> getOriginalTypes(DexType type) {
+ return getPrevious().getOriginalTypes(type);
+ }
+
+ @Override
public DexField getOriginalFieldSignature(DexField field) {
return getPrevious().getOriginalFieldSignature(field);
}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
index 9e23dd0..867ec0b 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
@@ -52,6 +52,11 @@
}
@Override
+ public Iterable<DexType> getOriginalTypes(DexType type) {
+ return getPrevious().getOriginalTypes(type);
+ }
+
+ @Override
public DexField getOriginalFieldSignature(DexField field) {
return getPrevious().getOriginalFieldSignature(field);
}
diff --git a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
index 9fe3e29..6a6c3bb 100644
--- a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
@@ -49,6 +49,11 @@
}
@Override
+ public Iterable<DexType> getOriginalTypes(DexType type) {
+ return getPrevious().getOriginalTypes(type);
+ }
+
+ @Override
public DexField getOriginalFieldSignature(DexField field) {
return getPrevious().getOriginalFieldSignature(field);
}
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 9b838df..bd537a6 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -18,7 +18,10 @@
import com.android.tools.r8.retrace.internal.RetraceRegularExpression;
import com.android.tools.r8.retrace.internal.RetracerImpl;
import com.android.tools.r8.retrace.internal.StackTraceElementProxyRetracerImpl;
+import com.android.tools.r8.retrace.internal.StackTraceElementProxyRetracerImpl.RetraceStackTraceProxyImpl;
import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy;
+import com.android.tools.r8.retrace.internal.StackTraceVisitor;
+import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.OptionsParsing;
import com.android.tools.r8.utils.OptionsParsing.ParseContext;
@@ -35,7 +38,9 @@
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Scanner;
/**
@@ -169,38 +174,53 @@
timing.end();
RetraceCommandLineResult result;
timing.begin("Parse and Retrace");
- if (command.regularExpression != null) {
- result =
- new RetraceRegularExpression(
- retracer,
- command.stackTrace,
- command.diagnosticsHandler,
- command.regularExpression,
- command.isVerbose)
- .retrace();
- } else {
- PlainStackTraceVisitor plainStackTraceVisitor =
- new PlainStackTraceVisitor(command.stackTrace, command.diagnosticsHandler);
+ StackTraceVisitor<StackTraceElementStringProxy> stackTraceVisitor =
+ command.regularExpression != null
+ ? new RetraceRegularExpression(
+ retracer, command.stackTrace, command.regularExpression)
+ : new PlainStackTraceVisitor(command.stackTrace, command.diagnosticsHandler);
StackTraceElementProxyRetracer<StackTraceElementStringProxy> proxyRetracer =
new StackTraceElementProxyRetracerImpl<>(retracer);
List<String> retracedStrings = new ArrayList<>();
- plainStackTraceVisitor.forEach(
- stackTraceElement -> {
- List<String> retracedStringsForElement = new ArrayList<>();
- proxyRetracer
- .retrace(stackTraceElement)
- .forEach(
- retracedElement -> {
- StackTraceElementStringProxy originalItem =
- retracedElement.getOriginalItem();
- retracedStringsForElement.add(
- originalItem.toRetracedItem(
- retracedElement, !retracedStringsForElement.isEmpty()));
- });
- retracedStrings.addAll(retracedStringsForElement);
- });
+ stackTraceVisitor.forEach(
+ stackTraceElement -> {
+ Box<List<RetraceStackTraceProxyImpl<StackTraceElementStringProxy>>> currentList =
+ new Box<>();
+ Map<
+ RetraceStackTraceProxyImpl<StackTraceElementStringProxy>,
+ List<RetraceStackTraceProxyImpl<StackTraceElementStringProxy>>>
+ ambiguousBlocks = new HashMap<>();
+ proxyRetracer
+ .retrace(stackTraceElement)
+ .forEach(
+ retracedElement -> {
+ if (retracedElement.isTopFrame() || !retracedElement.hasRetracedClass()) {
+ List<RetraceStackTraceProxyImpl<StackTraceElementStringProxy>> block =
+ new ArrayList<>();
+ ambiguousBlocks.put(retracedElement, block);
+ currentList.set(block);
+ }
+ currentList.get().add(retracedElement);
+ });
+ ambiguousBlocks.keySet().stream()
+ .sorted()
+ .forEach(
+ topFrame -> {
+ ambiguousBlocks
+ .get(topFrame)
+ .forEach(
+ frame -> {
+ StackTraceElementStringProxy originalItem = frame.getOriginalItem();
+ retracedStrings.add(
+ originalItem.toRetracedItem(
+ frame, !currentList.isSet(), command.isVerbose));
+ // Use the current list as indicator for us seeing the first
+ // sorted element.
+ currentList.set(null);
+ });
+ });
+ });
result = new RetraceCommandLineResult(retracedStrings);
- }
timing.end();
timing.begin("Report result");
command.retracedStackTraceConsumer.accept(result.getNodes());
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java
index f141080..d3b6014 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java
@@ -27,5 +27,7 @@
RetraceFieldResult getRetraceFieldResult();
RetraceClassResult.Element getClassElement();
+
+ RetraceSourceFileResult retraceSourceFile(String sourceFile);
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceProxy.java b/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceProxy.java
index b69c237..d9d1375 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceProxy.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceProxy.java
@@ -5,26 +5,42 @@
package com.android.tools.r8.retrace;
import com.android.tools.r8.Keep;
+import java.util.List;
@Keep
-public interface RetraceStackTraceProxy<T extends StackTraceElementProxy<?>> {
+public interface RetraceStackTraceProxy<T extends StackTraceElementProxy<?>>
+ extends Comparable<RetraceStackTraceProxy<T>> {
boolean isAmbiguous();
+ boolean isTopFrame();
+
boolean hasRetracedClass();
boolean hasRetracedMethod();
+ boolean hasRetracedField();
+
boolean hasSourceFile();
boolean hasLineNumber();
+ boolean hasFieldOrReturnType();
+
+ boolean hasMethodArguments();
+
T getOriginalItem();
RetracedClass getRetracedClass();
RetracedMethod getRetracedMethod();
+ RetracedField getRetracedField();
+
+ RetracedType getRetracedFieldOrReturnType();
+
+ List<RetracedType> getMethodArguments();
+
String getSourceFile();
int getLineNumber();
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java
index b9fa2d3..2c0c1fa 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.retrace;
import com.android.tools.r8.Keep;
-import com.android.tools.r8.retrace.internal.RetracedTypeImpl;
import java.util.function.Consumer;
import java.util.stream.Stream;
@@ -21,6 +20,6 @@
@Keep
interface Element {
- RetracedTypeImpl getType();
+ RetracedType getType();
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetracedClassMember.java b/src/main/java/com/android/tools/r8/retrace/RetracedClassMember.java
index 10aa77c..f28b87e 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetracedClassMember.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetracedClassMember.java
@@ -5,10 +5,9 @@
package com.android.tools.r8.retrace;
import com.android.tools.r8.Keep;
-import com.android.tools.r8.retrace.internal.RetracedClassImpl;
@Keep
public interface RetracedClassMember {
- RetracedClassImpl getHolderClass();
+ RetracedClass getHolderClass();
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetracedMethod.java b/src/main/java/com/android/tools/r8/retrace/RetracedMethod.java
index bfc95e9..936ea3e 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetracedMethod.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetracedMethod.java
@@ -10,7 +10,7 @@
import java.util.List;
@Keep
-public interface RetracedMethod extends RetracedClassMember {
+public interface RetracedMethod extends RetracedClassMember, Comparable<RetracedMethod> {
boolean isUnknown();
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 efaab05..09c1a8f 100644
--- a/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java
+++ b/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.retrace;
import com.android.tools.r8.Keep;
+import com.android.tools.r8.references.ClassReference;
@Keep
public abstract class StackTraceElementProxy<T> {
@@ -17,11 +18,23 @@
public abstract boolean hasLineNumber();
- public abstract String className();
+ public abstract boolean hasFieldName();
- public abstract String methodName();
+ public abstract boolean hasFieldOrReturnType();
- public abstract String fileName();
+ public abstract boolean hasMethodArguments();
- public abstract int lineNumber();
+ public abstract ClassReference getClassReference();
+
+ public abstract String getMethodName();
+
+ public abstract String getFileName();
+
+ public abstract int getLineNumber();
+
+ public abstract String getFieldName();
+
+ public abstract String getFieldOrReturnType();
+
+ public abstract String getMethodArguments();
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/FieldDefinition.java b/src/main/java/com/android/tools/r8/retrace/internal/FieldDefinition.java
index 4a3b93c..27d9a54 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/FieldDefinition.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/FieldDefinition.java
@@ -43,7 +43,7 @@
@Override
FieldDefinition substituteHolder(ClassReference newHolder) {
- return FieldDefinition.create(classReference, name);
+ return FieldDefinition.create(newHolder, name);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceVisitor.java b/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceVisitor.java
index 0aac05f..7f7d7fc 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceVisitor.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceVisitor.java
@@ -7,6 +7,7 @@
import static com.google.common.base.Predicates.not;
import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.ClassNameType;
import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.StackTraceElementStringProxyBuilder;
import com.android.tools.r8.utils.DescriptorUtils;
import java.util.List;
@@ -92,7 +93,7 @@
return null;
}
return StackTraceElementStringProxy.builder(line)
- .registerClassName(exceptionStartIndex, messageStartIndex)
+ .registerClassName(exceptionStartIndex, messageStartIndex, ClassNameType.TYPENAME)
.build();
}
}
@@ -161,7 +162,7 @@
}
StackTraceElementStringProxyBuilder builder =
StackTraceElementStringProxy.builder(line)
- .registerClassName(classStartIndex, methodSeparator)
+ .registerClassName(classStartIndex, methodSeparator, ClassNameType.TYPENAME)
.registerMethodName(methodSeparator + 1, parensStart);
// Check if we have a filename and position.
int separatorIndex = firstCharFromIndex(line, parensStart, ':');
@@ -201,7 +202,7 @@
return null;
}
return StackTraceElementStringProxy.builder(line)
- .registerClassName(exceptionStartIndex, lastBracketPosition)
+ .registerClassName(exceptionStartIndex, lastBracketPosition, ClassNameType.TYPENAME)
.build();
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
index 066b979..3a65b0a 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
@@ -9,7 +9,7 @@
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.retrace.RetraceFieldResult;
-import com.android.tools.r8.retrace.Retracer;
+import com.android.tools.r8.retrace.RetraceSourceFileResult;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.Pair;
import java.util.List;
@@ -22,13 +22,13 @@
private final RetraceClassResultImpl classResult;
private final List<Pair<RetraceClassResultImpl.ElementImpl, List<MemberNaming>>> memberNamings;
private final FieldDefinition fieldDefinition;
- private final Retracer retracer;
+ private final RetracerImpl retracer;
RetraceFieldResultImpl(
RetraceClassResultImpl classResult,
List<Pair<RetraceClassResultImpl.ElementImpl, List<MemberNaming>>> memberNamings,
FieldDefinition fieldDefinition,
- Retracer retracer) {
+ RetracerImpl retracer) {
this.classResult = classResult;
this.memberNamings = memberNamings;
this.fieldDefinition = fieldDefinition;
@@ -131,5 +131,11 @@
public RetraceClassResultImpl.ElementImpl getClassElement() {
return classElement;
}
+
+ @Override
+ public RetraceSourceFileResult retraceSourceFile(String sourceFile) {
+ return RetraceUtils.getSourceFile(
+ classElement, fieldReference.getHolderClass(), sourceFile, retraceFieldResult.retracer);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceRegularExpression.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceRegularExpression.java
index 6ba4d1a..ca9a2f8 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceRegularExpression.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceRegularExpression.java
@@ -4,42 +4,19 @@
package com.android.tools.r8.retrace.internal;
-import static com.android.tools.r8.retrace.internal.RetraceUtils.methodDescriptionFromRetraceMethod;
-
-import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.references.TypeReference;
-import com.android.tools.r8.retrace.RetraceClassResult;
-import com.android.tools.r8.retrace.RetraceFieldResult;
-import com.android.tools.r8.retrace.RetraceFrameResult;
-import com.android.tools.r8.retrace.RetraceFrameResult.Element;
-import com.android.tools.r8.retrace.RetraceSourceFileResult;
-import com.android.tools.r8.retrace.RetracedClass;
-import com.android.tools.r8.retrace.RetracedField;
-import com.android.tools.r8.retrace.RetracedField.KnownRetracedField;
-import com.android.tools.r8.retrace.RetracedMethod;
-import com.android.tools.r8.retrace.internal.RetraceRegularExpression.ClassNameGroup.ClassNameGroupHandler;
-import com.android.tools.r8.utils.Box;
-import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.StringUtils;
-import com.google.common.collect.Lists;
+import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.ClassNameType;
+import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.StackTraceElementStringProxyBuilder;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
-import java.util.Objects;
-import java.util.function.BiConsumer;
-import java.util.function.Function;
+import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-public class RetraceRegularExpression {
+public class RetraceRegularExpression implements StackTraceVisitor<StackTraceElementStringProxy> {
private final RetracerImpl retracer;
private final List<String> stackTrace;
- private final DiagnosticsHandler diagnosticsHandler;
private final String regularExpression;
private static final int NO_MATCH = -1;
@@ -52,28 +29,21 @@
private final LineNumberGroup lineNumberGroup = new LineNumberGroup();
private final FieldOrReturnTypeGroup fieldOrReturnTypeGroup = new FieldOrReturnTypeGroup();
private final MethodArgumentsGroup methodArgumentsGroup = new MethodArgumentsGroup();
-
- private final MethodNameGroup methodNameGroup;
- private final FieldNameGroup fieldNameGroup;
+ private final MethodNameGroup methodNameGroup = new MethodNameGroup();
+ private final FieldNameGroup fieldNameGroup = new FieldNameGroup();
private static final String CAPTURE_GROUP_PREFIX = "captureGroup";
private static final int FIRST_CAPTURE_GROUP_INDEX = 0;
public RetraceRegularExpression(
- RetracerImpl retracer,
- List<String> stackTrace,
- DiagnosticsHandler diagnosticsHandler,
- String regularExpression,
- boolean isVerbose) {
+ RetracerImpl retracer, List<String> stackTrace, String regularExpression) {
this.retracer = retracer;
this.stackTrace = stackTrace;
- this.diagnosticsHandler = diagnosticsHandler;
this.regularExpression = regularExpression;
- methodNameGroup = new MethodNameGroup(isVerbose);
- fieldNameGroup = new FieldNameGroup(isVerbose);
}
- public RetraceCommandLineResult retrace() {
+ @Override
+ public void forEach(Consumer<StackTraceElementStringProxy> consumer) {
List<RegularExpressionGroupHandler> handlers = new ArrayList<>();
StringBuilder refinedRegularExpressionBuilder = new StringBuilder();
registerGroups(
@@ -83,74 +53,22 @@
FIRST_CAPTURE_GROUP_INDEX);
String refinedRegularExpression = refinedRegularExpressionBuilder.toString();
Pattern compiledPattern = Pattern.compile(refinedRegularExpression);
- List<String> result = new ArrayList<>();
for (String string : stackTrace) {
+ StackTraceElementStringProxyBuilder proxyBuilder =
+ StackTraceElementStringProxy.builder(string);
Matcher matcher = compiledPattern.matcher(string);
- if (!matcher.matches()) {
- result.add(string);
- continue;
- }
- // Iterate through handlers to set contexts. That will allow us to process all handlers from
- // left to right.
- RetraceStringContext initialContext = RetraceStringContext.empty();
- for (RegularExpressionGroupHandler handler : handlers) {
- initialContext = handler.buildInitial(initialContext, matcher, retracer);
- }
- final RetraceString initialRetraceString = RetraceString.start(initialContext);
- List<RetraceString> retracedStrings = Lists.newArrayList(initialRetraceString);
- for (RegularExpressionGroupHandler handler : handlers) {
- retracedStrings = handler.handleMatch(string, retracedStrings, matcher, retracer);
- }
- if (retracedStrings.isEmpty()) {
- // We could not find a match. Output the identity.
- result.add(string);
- }
- boolean isAmbiguous = retracedStrings.size() > 1 && retracedStrings.get(0).isAmbiguous();
- if (isAmbiguous) {
- retracedStrings.sort(new RetraceLineComparator());
- }
- RetracedClass previousContext = null;
- for (RetraceString retracedString : retracedStrings) {
- String finalString = retracedString.builder.build(string);
- if (!isAmbiguous) {
- result.add(finalString);
- continue;
+ if (matcher.matches()) {
+ boolean seenMatchedClassHandler = false;
+ for (RegularExpressionGroupHandler handler : handlers) {
+ if (seenMatchedClassHandler && handler.isClassHandler()) {
+ continue;
+ }
+ if (handler.matchHandler(proxyBuilder, matcher)) {
+ seenMatchedClassHandler |= handler.isClassHandler();
+ }
}
- assert retracedString.getClassContext() != null;
- RetracedClass currentContext = retracedString.getClassContext().getRetracedClass();
- if (currentContext.equals(previousContext)) {
- int firstNonWhitespaceCharacter = StringUtils.firstNonWhitespaceCharacter(finalString);
- finalString =
- finalString.substring(0, firstNonWhitespaceCharacter)
- + "<OR> "
- + finalString.substring(firstNonWhitespaceCharacter);
- }
- previousContext = currentContext;
- result.add(finalString);
}
- }
- return new RetraceCommandLineResult(result);
- }
-
- static class RetraceLineComparator extends AmbiguousComparator<RetraceString> {
-
- RetraceLineComparator() {
- super(
- (line, t) -> {
- switch (t) {
- case CLASS:
- return line.getClassContext().getRetracedClass().getTypeName();
- case METHOD:
- return line.getMethodContext().getTopFrame().getMethodName();
- case SOURCE:
- return line.getSource();
- case LINE:
- return line.getLineNumber() + "";
- default:
- assert false;
- }
- throw new RuntimeException("Comparator key is unknown");
- });
+ consumer.accept(proxyBuilder.build());
}
}
@@ -160,7 +78,6 @@
List<RegularExpressionGroupHandler> handlers,
int captureGroupIndex) {
int lastCommittedIndex = 0;
- RegularExpressionGroupHandler lastHandler = null;
boolean seenPercentage = false;
boolean escaped = false;
for (int i = 0; i < regularExpression.length(); i++) {
@@ -180,23 +97,7 @@
.append(">")
.append(group.subExpression())
.append(")");
- final RegularExpressionGroupHandler handler = group.createHandler(captureGroupName);
- // If we see a pattern as %c.%m or %C/%m, then register the groups to allow delaying
- // writing of the class string until we have the fully qualified member.
- if (lastHandler != null
- && handler.isQualifiedHandler()
- && lastHandler.isClassNameGroupHandler()
- && lastCommittedIndex == i - 3
- && isTypeOrBinarySeparator(regularExpression, lastCommittedIndex, i - 2)) {
- final ClassNameGroupHandler classNameGroupHandler =
- lastHandler.asClassNameGroupHandler();
- final QualifiedRegularExpressionGroupHandler qualifiedHandler =
- handler.asQualifiedHandler();
- classNameGroupHandler.setQualifiedHandler(qualifiedHandler);
- qualifiedHandler.setClassNameGroupHandler(classNameGroupHandler);
- }
- lastHandler = handler;
- handlers.add(handler);
+ handlers.add(group.createHandler(captureGroupName));
}
lastCommittedIndex = i + 1;
seenPercentage = false;
@@ -247,250 +148,13 @@
}
}
- static class RetraceStringContext {
- private final RetraceClassResult.Element classContext;
- private final RetraceFrameResult.Element methodContext;
- private final RetracedClass qualifiedClassContext;
- private final RetracedMethod qualifiedMethodContext;
- private final String methodName;
- private final int minifiedLineNumber;
- private final int originalLineNumber;
- private final String source;
- private final boolean isAmbiguous;
-
- private RetraceStringContext(
- RetraceClassResult.Element classContext,
- RetraceFrameResult.Element methodContext,
- RetracedClass qualifiedClassContext,
- RetracedMethod qualifiedMethodContext,
- String methodName,
- int minifiedLineNumber,
- int originalLineNumber,
- String source,
- boolean isAmbiguous) {
- this.classContext = classContext;
- this.methodContext = methodContext;
- this.qualifiedClassContext = qualifiedClassContext;
- this.qualifiedMethodContext = qualifiedMethodContext;
- this.methodName = methodName;
- this.minifiedLineNumber = minifiedLineNumber;
- this.originalLineNumber = originalLineNumber;
- this.source = source;
- this.isAmbiguous = isAmbiguous;
- }
-
- private static RetraceStringContext empty() {
- return new RetraceStringContext(
- null, null, null, null, null, NO_MATCH, NO_MATCH, null, false);
- }
-
- private RetraceStringContext withClassContext(
- RetraceClassResult.Element classContext, RetracedClass qualifiedContext) {
- return new RetraceStringContext(
- classContext,
- methodContext,
- qualifiedContext,
- qualifiedMethodContext,
- methodName,
- minifiedLineNumber,
- originalLineNumber,
- source,
- isAmbiguous);
- }
-
- private RetraceStringContext withMethodName(String methodName) {
- return new RetraceStringContext(
- classContext,
- methodContext,
- qualifiedClassContext,
- qualifiedMethodContext,
- methodName,
- minifiedLineNumber,
- originalLineNumber,
- source,
- isAmbiguous);
- }
-
- private RetraceStringContext withMethodContext(Element methodContext, boolean isAmbiguous) {
- return new RetraceStringContext(
- classContext,
- methodContext,
- qualifiedClassContext,
- qualifiedMethodContext,
- methodName,
- minifiedLineNumber,
- originalLineNumber,
- source,
- isAmbiguous);
- }
-
- private RetraceStringContext withQualifiedClassContext(RetracedClass qualifiedContext) {
- return new RetraceStringContext(
- classContext,
- methodContext,
- qualifiedContext,
- qualifiedMethodContext,
- methodName,
- minifiedLineNumber,
- originalLineNumber,
- source,
- isAmbiguous);
- }
-
- private RetraceStringContext withQualifiedMethodContext(RetracedMethod qualifiedContext) {
- return new RetraceStringContext(
- classContext,
- methodContext,
- qualifiedContext.getHolderClass(),
- qualifiedContext,
- methodName,
- minifiedLineNumber,
- originalLineNumber,
- source,
- isAmbiguous);
- }
-
- public RetraceStringContext withSource(String source) {
- return new RetraceStringContext(
- classContext,
- methodContext,
- qualifiedClassContext,
- qualifiedMethodContext,
- methodName,
- minifiedLineNumber,
- originalLineNumber,
- source,
- isAmbiguous);
- }
-
- public RetraceStringContext withLineNumbers(int minifiedLineNumber, int originalLineNumber) {
- return new RetraceStringContext(
- classContext,
- methodContext,
- qualifiedClassContext,
- qualifiedMethodContext,
- methodName,
- minifiedLineNumber,
- originalLineNumber,
- source,
- isAmbiguous);
- }
- }
-
- static class RetraceStringBuilder {
-
- private final StringBuilder retracedString;
- private int lastCommittedIndex;
-
- private RetraceStringBuilder(String retracedString, int lastCommittedIndex) {
- this.retracedString = new StringBuilder(retracedString);
- this.lastCommittedIndex = lastCommittedIndex;
- }
-
- private void appendRetracedString(
- String source, String stringToAppend, int originalFromIndex, int originalToIndex) {
- retracedString.append(source, lastCommittedIndex, originalFromIndex);
- retracedString.append(stringToAppend);
- lastCommittedIndex = originalToIndex;
- }
-
- private String build(String source) {
- return retracedString.append(source, lastCommittedIndex, source.length()).toString();
- }
- }
-
- private static class RetraceString {
-
- private final RetraceStringBuilder builder;
- private final RetraceStringContext context;
-
- private RetraceString(RetraceStringBuilder builder, RetraceStringContext context) {
- this.builder = builder;
- this.context = context;
- }
-
- private RetraceClassResult.Element getClassContext() {
- return context.classContext;
- }
-
- private RetraceFrameResult.Element getMethodContext() {
- return context.methodContext;
- }
-
- private String getSource() {
- return context.source;
- }
-
- private int getLineNumber() {
- return context.originalLineNumber;
- }
-
- private boolean isAmbiguous() {
- return context.isAmbiguous;
- }
-
- private static RetraceString start(RetraceStringContext initialContext) {
- return new RetraceString(new RetraceStringBuilder("", 0), initialContext);
- }
-
- private RetraceString updateContext(
- Function<RetraceStringContext, RetraceStringContext> update) {
- return new RetraceString(builder, update.apply(context));
- }
-
- private RetraceString duplicate(RetraceStringContext newContext) {
- return new RetraceString(
- new RetraceStringBuilder(builder.retracedString.toString(), builder.lastCommittedIndex),
- newContext);
- }
-
- private RetraceString appendRetracedString(
- String source, String stringToAppend, int originalFromIndex, int originalToIndex) {
- builder.appendRetracedString(source, stringToAppend, originalFromIndex, originalToIndex);
- return this;
- }
- }
-
private interface RegularExpressionGroupHandler {
- List<RetraceString> handleMatch(
- String original, List<RetraceString> strings, Matcher matcher, RetracerImpl retracer);
+ boolean matchHandler(StackTraceElementStringProxyBuilder builder, Matcher matcher);
- default RetraceStringContext buildInitial(
- RetraceStringContext context, Matcher matcher, RetracerImpl retracer) {
- return context;
- }
-
- default boolean isClassNameGroupHandler() {
+ default boolean isClassHandler() {
return false;
}
-
- default ClassNameGroupHandler asClassNameGroupHandler() {
- return null;
- }
-
- default boolean isQualifiedHandler() {
- return false;
- }
-
- default QualifiedRegularExpressionGroupHandler asQualifiedHandler() {
- return null;
- }
- }
-
- private interface QualifiedRegularExpressionGroupHandler extends RegularExpressionGroupHandler {
-
- @Override
- default boolean isQualifiedHandler() {
- return true;
- }
-
- @Override
- default QualifiedRegularExpressionGroupHandler asQualifiedHandler() {
- return this;
- }
-
- void setClassNameGroupHandler(ClassNameGroupHandler handler);
}
private abstract static class RegularExpressionGroup {
@@ -513,107 +177,31 @@
abstract static class ClassNameGroup extends RegularExpressionGroup {
- abstract String getClassName(RetracedClass classReference);
-
- abstract ClassReference classFromMatch(String match);
+ abstract ClassNameType getClassNameType();
@Override
RegularExpressionGroupHandler createHandler(String captureGroup) {
- return new ClassNameGroupHandler(this, captureGroup);
- }
-
- static class ClassNameGroupHandler implements RegularExpressionGroupHandler {
-
- private RetraceClassResultImpl retraceClassResult = null;
- private final ClassNameGroup classNameGroup;
- private final String captureGroup;
- private RegularExpressionGroupHandler qualifiedHandler;
-
- public ClassNameGroupHandler(ClassNameGroup classNameGroup, String captureGroup) {
- this.classNameGroup = classNameGroup;
- this.captureGroup = captureGroup;
- }
-
- @Override
- public List<RetraceString> handleMatch(
- String original, List<RetraceString> strings, Matcher matcher, RetracerImpl retracer) {
- final int startOfGroup = matcher.start(captureGroup);
- if (startOfGroup == NO_MATCH) {
- return strings;
+ return new RegularExpressionGroupHandler() {
+ @Override
+ public boolean matchHandler(StackTraceElementStringProxyBuilder builder, Matcher matcher) {
+ int startOfGroup = matcher.start(captureGroup);
+ if (startOfGroup == NO_MATCH) {
+ return false;
+ }
+ String typeName = matcher.group(captureGroup);
+ if (typeName.equals("Suppressed")) {
+ // Ensure we do not map supressed.
+ return false;
+ }
+ builder.registerClassName(startOfGroup, matcher.end(captureGroup), getClassNameType());
+ return true;
}
- String typeName = matcher.group(captureGroup);
- RetraceClassResultImpl retraceResult =
- retraceClassResult == null
- ? retracer.retraceClass(classNameGroup.classFromMatch(typeName))
- : retraceClassResult;
- assert !retraceResult.isAmbiguous();
- List<RetraceString> retraceStrings = new ArrayList<>(strings.size());
- for (RetraceString retraceString : strings) {
- retraceResult.forEach(
- element -> {
- RetraceString newRetraceString =
- retraceString.updateContext(
- context -> context.withClassContext(element, element.getRetracedClass()));
- retraceStrings.add(newRetraceString);
- if (qualifiedHandler == null) {
- // If there is no qualified handler, commit right away.
- newRetraceString.builder.appendRetracedString(
- original,
- classNameGroup.getClassName(element.getRetracedClass()),
- startOfGroup,
- matcher.end(captureGroup));
- }
- });
+
+ @Override
+ public boolean isClassHandler() {
+ return true;
}
- return retraceStrings;
- }
-
- void commitClassName(
- String original,
- RetraceString retraceString,
- RetracedClass qualifiedContext,
- Matcher matcher) {
- if (matcher.start(captureGroup) == NO_MATCH) {
- return;
- }
- retraceString.builder.appendRetracedString(
- original,
- classNameGroup.getClassName(qualifiedContext),
- matcher.start(captureGroup),
- matcher.end(captureGroup));
- }
-
- @Override
- public RetraceStringContext buildInitial(
- RetraceStringContext context, Matcher matcher, RetracerImpl retracer) {
- // Reset the local class context since this the same handler is used for multiple lines.
- retraceClassResult = null;
- if (matcher.start(captureGroup) == NO_MATCH || context.classContext != null) {
- return context;
- }
- String typeName = matcher.group(captureGroup);
- retraceClassResult = retracer.retraceClass(classNameGroup.classFromMatch(typeName));
- assert !retraceClassResult.isAmbiguous();
- Box<RetraceStringContext> box = new Box<>();
- retraceClassResult.forEach(
- element -> box.set(context.withClassContext(element, element.getRetracedClass())));
- return box.get();
- }
-
- public void setQualifiedHandler(RegularExpressionGroupHandler handler) {
- assert handler.isQualifiedHandler();
- this.qualifiedHandler = handler;
- }
-
- @Override
- public boolean isClassNameGroupHandler() {
- return true;
- }
-
- @Override
- public ClassNameGroupHandler asClassNameGroupHandler() {
- return this;
- }
+ };
}
}
@@ -625,13 +213,8 @@
}
@Override
- String getClassName(RetracedClass classReference) {
- return classReference.getTypeName();
- }
-
- @Override
- ClassReference classFromMatch(String match) {
- return Reference.classFromTypeName(match);
+ ClassNameType getClassNameType() {
+ return ClassNameType.TYPENAME;
}
}
@@ -643,24 +226,13 @@
}
@Override
- String getClassName(RetracedClass classReference) {
- return classReference.getBinaryName();
- }
-
- @Override
- ClassReference classFromMatch(String match) {
- return Reference.classFromBinaryName(match);
+ ClassNameType getClassNameType() {
+ return ClassNameType.BINARY;
}
}
private static class MethodNameGroup extends RegularExpressionGroup {
- private final boolean printVerbose;
-
- public MethodNameGroup(boolean printVerbose) {
- this.printVerbose = printVerbose;
- }
-
@Override
String subExpression() {
return METHOD_NAME_REGULAR_EXPRESSION;
@@ -668,98 +240,19 @@
@Override
RegularExpressionGroupHandler createHandler(String captureGroup) {
- return new QualifiedRegularExpressionGroupHandler() {
-
- private ClassNameGroupHandler classNameGroupHandler;
-
- @Override
- public void setClassNameGroupHandler(ClassNameGroupHandler handler) {
- classNameGroupHandler = handler;
+ return (builder, matcher) -> {
+ int startOfGroup = matcher.start(captureGroup);
+ if (startOfGroup == NO_MATCH) {
+ return false;
}
-
- @Override
- public List<RetraceString> handleMatch(
- String original, List<RetraceString> strings, Matcher matcher, RetracerImpl retracer) {
- final int startOfGroup = matcher.start(captureGroup);
- if (startOfGroup == NO_MATCH) {
- if (classNameGroupHandler != null) {
- for (RetraceString string : strings) {
- classNameGroupHandler.commitClassName(
- original, string, string.context.qualifiedClassContext, matcher);
- }
- }
- return strings;
- }
- String methodName = matcher.group(captureGroup);
- List<RetraceString> retracedStrings = new ArrayList<>();
- for (RetraceString retraceString : strings) {
- retraceMethodForString(
- retraceString,
- methodName,
- (element, newContext) -> {
- element.visitFrames(
- (method, ignoredPosition) -> {
- RetraceString newRetraceString =
- retraceString.duplicate(newContext.withQualifiedMethodContext(method));
- if (classNameGroupHandler != null) {
- classNameGroupHandler.commitClassName(
- original, newRetraceString, method.getHolderClass(), matcher);
- }
- retracedStrings.add(
- newRetraceString.appendRetracedString(
- original,
- printVerbose
- ? methodDescriptionFromRetraceMethod(method, false, true)
- : method.getMethodName(),
- startOfGroup,
- matcher.end(captureGroup)));
- });
- });
- }
- return retracedStrings;
- }
-
- @Override
- public RetraceStringContext buildInitial(
- RetraceStringContext context, Matcher matcher, RetracerImpl retracer) {
- final int startOfGroup = matcher.start(captureGroup);
- if (startOfGroup == NO_MATCH || context.methodName != null) {
- return context;
- }
- return context.withMethodName(matcher.group(captureGroup));
- }
+ builder.registerMethodName(startOfGroup, matcher.end(captureGroup));
+ return true;
};
}
-
- private static void retraceMethodForString(
- RetraceString retraceString,
- String methodName,
- BiConsumer<Element, RetraceStringContext> process) {
- if (retraceString.context.classContext == null) {
- return;
- }
- RetraceClassResult.Element classContext = retraceString.getClassContext();
- RetraceFrameResult retraceFrameResult =
- retraceString.context.minifiedLineNumber > NO_MATCH
- ? classContext.lookupFrame(methodName, retraceString.context.minifiedLineNumber)
- : classContext.lookupFrame(methodName);
- retraceFrameResult.forEach(
- element -> {
- process.accept(
- element,
- retraceString.context.withMethodContext(element, retraceFrameResult.isAmbiguous()));
- });
- }
}
private static class FieldNameGroup extends RegularExpressionGroup {
- private final boolean printVerbose;
-
- public FieldNameGroup(boolean printVerbose) {
- this.printVerbose = printVerbose;
- }
-
@Override
String subExpression() {
return javaIdentifierSegment;
@@ -767,70 +260,15 @@
@Override
RegularExpressionGroupHandler createHandler(String captureGroup) {
- return new QualifiedRegularExpressionGroupHandler() {
-
- private ClassNameGroupHandler classNameGroupHandler;
-
- @Override
- public void setClassNameGroupHandler(ClassNameGroupHandler handler) {
- classNameGroupHandler = handler;
+ return (builder, matcher) -> {
+ int startOfGroup = matcher.start(captureGroup);
+ if (startOfGroup == NO_MATCH) {
+ return false;
}
-
- @Override
- public List<RetraceString> handleMatch(
- String original, List<RetraceString> strings, Matcher matcher, RetracerImpl retracer) {
- final int startOfGroup = matcher.start(captureGroup);
- if (startOfGroup == NO_MATCH) {
- if (classNameGroupHandler != null) {
- for (RetraceString string : strings) {
- classNameGroupHandler.commitClassName(
- original, string, string.context.qualifiedClassContext, matcher);
- }
- }
- return strings;
- }
- String fieldName = matcher.group(captureGroup);
- List<RetraceString> retracedStrings = new ArrayList<>();
- for (RetraceString retraceString : strings) {
- if (retraceString.getClassContext() == null) {
- assert classNameGroupHandler == null;
- return strings;
- }
- final RetraceFieldResult retraceFieldResult =
- retraceString.getClassContext().lookupField(fieldName);
- assert !retraceFieldResult.isAmbiguous();
- retraceFieldResult.forEach(
- element -> {
- if (classNameGroupHandler != null) {
- classNameGroupHandler.commitClassName(
- original, retraceString, element.getField().getHolderClass(), matcher);
- }
- retracedStrings.add(
- retraceString
- .updateContext(
- context ->
- context.withQualifiedClassContext(
- element.getField().getHolderClass()))
- .appendRetracedString(
- original,
- getFieldString(element.getField()),
- startOfGroup,
- matcher.end(captureGroup)));
- });
- }
- return retracedStrings;
- }
+ builder.registerFieldName(startOfGroup, matcher.end(captureGroup));
+ return true;
};
}
-
- private String getFieldString(RetracedField fieldReference) {
- if (!printVerbose || fieldReference.isUnknown()) {
- return fieldReference.getFieldName();
- }
- assert fieldReference.isKnown();
- KnownRetracedField knownRef = fieldReference.asKnown();
- return knownRef.getFieldType().getTypeName() + " " + fieldReference.getFieldName();
- }
}
private static class SourceFileGroup extends RegularExpressionGroup {
@@ -842,40 +280,13 @@
@Override
RegularExpressionGroupHandler createHandler(String captureGroup) {
- return (original, strings, matcher, retracer) -> {
- final int startOfGroup = matcher.start(captureGroup);
+ return (builder, matcher) -> {
+ int startOfGroup = matcher.start(captureGroup);
if (startOfGroup == NO_MATCH) {
- return strings;
+ return false;
}
- String fileName = matcher.group(captureGroup);
- List<RetraceString> retracedStrings = null;
- for (RetraceString retraceString : strings) {
- if (retraceString.context.classContext == null) {
- return strings;
- }
- if (retracedStrings == null) {
- retracedStrings = new ArrayList<>();
- }
- RetraceSourceFileResult sourceFileResult =
- retraceString.getMethodContext() != null
- ? retraceString
- .getMethodContext()
- .retraceSourceFile(retraceString.context.qualifiedMethodContext, fileName)
- : RetraceUtils.getSourceFile(
- retraceString.getClassContext(),
- retraceString.context.qualifiedClassContext,
- fileName,
- retracer);
- retracedStrings.add(
- retraceString
- .updateContext(context -> context.withSource(sourceFileResult.getFilename()))
- .appendRetracedString(
- original,
- sourceFileResult.getFilename(),
- startOfGroup,
- matcher.end(captureGroup)));
- }
- return retracedStrings;
+ builder.registerSourceFile(startOfGroup, matcher.end(captureGroup));
+ return true;
};
}
}
@@ -889,88 +300,13 @@
@Override
RegularExpressionGroupHandler createHandler(String captureGroup) {
- return new RegularExpressionGroupHandler() {
- @Override
- public List<RetraceString> handleMatch(
- String original, List<RetraceString> strings, Matcher matcher, RetracerImpl retracer) {
- final int startOfGroup = matcher.start(captureGroup);
- if (startOfGroup == NO_MATCH) {
- return strings;
- }
- String lineNumberAsString = matcher.group(captureGroup);
- if (lineNumberAsString.isEmpty()) {
- return strings;
- }
- int lineNumber = Integer.parseInt(lineNumberAsString);
- List<RetraceString> retracedStrings = new ArrayList<>();
- for (RetraceString retraceString : strings) {
- Element methodContext = retraceString.context.methodContext;
- if (methodContext == null) {
- if (retraceString.context.classContext == null
- || retraceString.context.methodName == null) {
- // We have no way of retracing the line number.
- retracedStrings.add(retraceString);
- continue;
- }
- // This situation arises when we have a matched pattern as %l..%c.%m where the
- // line number handler is defined before the methodname handler.
- MethodNameGroup.retraceMethodForString(
- retraceString,
- retraceString.context.methodName,
- (element, newContext) -> {
- // The same method can be represented multiple times if it has multiple
- // mappings.
- element.visitFrames(
- (method, ignoredPosition) -> {
- int originalPosition = method.getOriginalPositionOrDefault(lineNumber);
- retracedStrings.add(
- retraceString
- .duplicate(
- retraceString
- .context
- .withQualifiedMethodContext(method)
- .withLineNumbers(lineNumber, originalPosition))
- .appendRetracedString(
- original,
- originalPosition + "",
- startOfGroup,
- matcher.end(captureGroup)));
- });
- });
- continue;
- }
- // If the method context is unknown, do nothing.
- if (methodContext.isUnknown()) {
- retracedStrings.add(retraceString);
- continue;
- }
- int originalLineNumber =
- retraceString.context.qualifiedMethodContext.getOriginalPositionOrDefault(
- lineNumber);
- retracedStrings.add(
- retraceString
- .updateContext(
- context -> context.withLineNumbers(lineNumber, originalLineNumber))
- .appendRetracedString(
- original,
- originalLineNumber + "",
- startOfGroup,
- matcher.end(captureGroup)));
- }
- return retracedStrings;
+ return (builder, matcher) -> {
+ int startOfGroup = matcher.start(captureGroup);
+ if (startOfGroup == NO_MATCH) {
+ return false;
}
-
- @Override
- public RetraceStringContext buildInitial(
- RetraceStringContext context, Matcher matcher, RetracerImpl retracer) {
- if (matcher.start(captureGroup) == NO_MATCH || context.minifiedLineNumber > NO_MATCH) {
- return context;
- }
- String lineNumberAsString = matcher.group(captureGroup);
- return context.withLineNumbers(
- lineNumberAsString.isEmpty() ? NO_MATCH : Integer.parseInt(lineNumberAsString),
- NO_MATCH);
- }
+ builder.registerLineNumber(startOfGroup, matcher.end(captureGroup));
+ return true;
};
}
}
@@ -1005,31 +341,13 @@
@Override
RegularExpressionGroupHandler createHandler(String captureGroup) {
- return (original, strings, matcher, retracer) -> {
- final int startOfGroup = matcher.start(captureGroup);
+ return (builder, matcher) -> {
+ int startOfGroup = matcher.start(captureGroup);
if (startOfGroup == NO_MATCH) {
- return strings;
+ return false;
}
- String typeName = matcher.group(captureGroup);
- String descriptor = DescriptorUtils.javaTypeToDescriptor(typeName);
- if (!DescriptorUtils.isDescriptor(descriptor) && !"V".equals(descriptor)) {
- return strings;
- }
- TypeReference typeReference = Reference.returnTypeFromDescriptor(descriptor);
- RetraceTypeResultImpl retracedType = retracer.retraceType(typeReference);
- assert !retracedType.isAmbiguous();
- for (RetraceString retraceString : strings) {
- retracedType.forEach(
- element -> {
- RetracedTypeImpl retracedReference = element.getType();
- retraceString.appendRetracedString(
- original,
- retracedReference.isVoid() ? "void" : retracedReference.getTypeName(),
- startOfGroup,
- matcher.end(captureGroup));
- });
- }
- return strings;
+ builder.registerFieldOrReturnType(startOfGroup, matcher.end(captureGroup));
+ return true;
};
}
}
@@ -1043,37 +361,15 @@
@Override
RegularExpressionGroupHandler createHandler(String captureGroup) {
- return (original, strings, matcher, retracer) -> {
- final int startOfGroup = matcher.start(captureGroup);
+ return (builder, matcher) -> {
+ int startOfGroup = matcher.start(captureGroup);
if (startOfGroup == NO_MATCH) {
- return strings;
+ return false;
}
- final String formals =
- Arrays.stream(matcher.group(captureGroup).split(","))
- .map(
- typeName -> {
- typeName = typeName.trim();
- if (typeName.isEmpty()) {
- return null;
- }
- String descriptor = DescriptorUtils.javaTypeToDescriptor(typeName);
- if (!DescriptorUtils.isDescriptor(descriptor) && !"V".equals(descriptor)) {
- return typeName;
- }
- final RetraceTypeResultImpl retraceResult =
- retracer.retraceType(Reference.returnTypeFromDescriptor(descriptor));
- assert !retraceResult.isAmbiguous();
- final Box<RetracedTypeImpl> elementBox = new Box<>();
- retraceResult.forEach(element -> elementBox.set(element.getType()));
- return elementBox.get().getTypeName();
- })
- .filter(Objects::nonNull)
- .collect(Collectors.joining(","));
- for (RetraceString string : strings) {
- string.appendRetracedString(original, formals, startOfGroup, matcher.end(captureGroup));
- }
- return strings;
+ builder.registerMethodArguments(startOfGroup, matcher.end(captureGroup));
+ return true;
};
}
}
}
+
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetracedMethodImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetracedMethodImpl.java
index 998fe7d..1f4ad68 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetracedMethodImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetracedMethodImpl.java
@@ -8,6 +8,8 @@
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.retrace.RetracedMethod;
+import com.android.tools.r8.utils.ComparatorUtils;
+import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -34,6 +36,27 @@
return null;
}
+ @Override
+ public int compareTo(RetracedMethod other) {
+ return Comparator.comparing(RetracedMethod::getMethodName)
+ .thenComparing(RetracedMethod::isKnown)
+ .thenComparing(
+ RetracedMethod::asKnown,
+ Comparator.nullsFirst(
+ Comparator.comparing(
+ (KnownRetracedMethod m) -> {
+ if (m == null) {
+ return null;
+ }
+ return m.isVoid() ? "void" : m.getReturnType().getTypeName();
+ }))
+ .thenComparing(
+ KnownRetracedMethod::getFormalTypes,
+ ComparatorUtils.listComparator(
+ Comparator.comparing(TypeReference::getTypeName))))
+ .compare(this, other);
+ }
+
public static final class KnownRetracedMethodImpl extends RetracedMethodImpl
implements KnownRetracedMethod {
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetracedTypeImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetracedTypeImpl.java
index 8162e05..d467cc1 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetracedTypeImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetracedTypeImpl.java
@@ -23,6 +23,10 @@
return new RetracedTypeImpl(typeReference);
}
+ static RetracedType createVoid() {
+ return new RetracedTypeImpl(null);
+ }
+
@Override
public boolean isVoid() {
return typeReference == null;
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
index c21b92d..a949fc1 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
@@ -5,13 +5,23 @@
package com.android.tools.r8.retrace.internal;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.retrace.RetraceClassResult;
+import com.android.tools.r8.retrace.RetraceFieldResult;
import com.android.tools.r8.retrace.RetraceStackTraceProxy;
import com.android.tools.r8.retrace.RetracedClass;
+import com.android.tools.r8.retrace.RetracedField;
import com.android.tools.r8.retrace.RetracedMethod;
+import com.android.tools.r8.retrace.RetracedType;
import com.android.tools.r8.retrace.StackTraceElementProxy;
import com.android.tools.r8.retrace.StackTraceElementProxyRetracer;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.ListUtils;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StackTraceElementProxyRetracerImpl<T extends StackTraceElementProxy<?>>
@@ -28,47 +38,188 @@
if (!element.hasClassName()) {
return Stream.of(RetraceStackTraceProxyImpl.builder(element).build());
}
- RetraceClassResultImpl classResult =
- retracer.retraceClass(Reference.classFromTypeName(element.className()));
- List<RetraceStackTraceProxyImpl<T>> retracedProxies = new ArrayList<>();
- if (!element.hasMethodName()) {
- classResult.forEach(
- classElement -> {
- RetraceStackTraceProxyImpl.Builder<T> proxy =
- RetraceStackTraceProxyImpl.builder(element)
- .setRetracedClassElement(classElement.getRetracedClass())
- .setAmbiguous(classResult.isAmbiguous());
- if (element.hasFileName()) {
- proxy.setSourceFile(classElement.retraceSourceFile(element.fileName()).getFilename());
- }
- retracedProxies.add(proxy.build());
- });
+ RetraceClassResultImpl classResult = retracer.retraceClass(element.getClassReference());
+ if (element.hasMethodName()) {
+ return retraceMethod(element, classResult);
+ } else if (element.hasFieldName()) {
+ return retraceField(element, classResult);
} else {
- RetraceFrameResultImpl frameResult =
- element.hasLineNumber()
- ? classResult.lookupFrame(element.methodName(), element.lineNumber())
- : classResult.lookupFrame(element.methodName());
- frameResult.forEach(
- frameElement -> {
- frameElement.visitFrames(
- (frame, index) -> {
- RetraceStackTraceProxyImpl.Builder<T> proxy =
- RetraceStackTraceProxyImpl.builder(element)
- .setRetracedClassElement(frame.getHolderClass())
- .setRetracedMethodElement(frame)
- .setAmbiguous(frameResult.isAmbiguous() && index == 0);
- if (element.hasLineNumber()) {
- proxy.setLineNumber(frame.getOriginalPositionOrDefault(element.lineNumber()));
- }
- if (element.hasFileName()) {
- proxy.setSourceFile(
- frameElement.retraceSourceFile(frame, element.fileName()).getFilename());
- }
- retracedProxies.add(proxy.build());
- });
- });
+ return retraceClassOrType(element, classResult);
}
- return retracedProxies.stream();
+ }
+
+ private Stream<RetraceStackTraceProxyImpl<T>> retraceClassOrType(
+ T element, RetraceClassResult classResult) {
+ return classResult.stream()
+ .flatMap(
+ classElement ->
+ retraceFieldOrReturnType(element)
+ .flatMap(
+ fieldOrReturnTypeConsumer ->
+ retracedMethodArguments(element)
+ .map(
+ argumentsConsumer -> {
+ RetraceStackTraceProxyImpl.Builder<T> proxy =
+ RetraceStackTraceProxyImpl.builder(element)
+ .setRetracedClass(classElement.getRetracedClass())
+ .setAmbiguous(classResult.isAmbiguous())
+ .setTopFrame(true);
+ argumentsConsumer.accept(proxy);
+ fieldOrReturnTypeConsumer.accept(proxy);
+ if (element.hasFileName()) {
+ proxy.setSourceFile(
+ classElement
+ .retraceSourceFile(element.getFileName())
+ .getFilename());
+ }
+ return proxy.build();
+ })));
+ }
+
+ private Stream<RetraceStackTraceProxyImpl<T>> retraceMethod(
+ T element, RetraceClassResultImpl classResult) {
+ return retraceFieldOrReturnType(element)
+ .flatMap(
+ fieldOrReturnTypeConsumer ->
+ retracedMethodArguments(element)
+ .flatMap(
+ argumentsConsumer -> {
+ RetraceFrameResultImpl frameResult =
+ element.hasLineNumber()
+ ? classResult.lookupFrame(
+ element.getMethodName(), element.getLineNumber())
+ : classResult.lookupFrame(element.getMethodName());
+ return frameResult.stream()
+ .flatMap(
+ frameElement -> {
+ List<RetraceStackTraceProxyImpl<T>> retracedProxies =
+ new ArrayList<>();
+ frameElement.visitFrames(
+ (frame, index) -> {
+ RetraceStackTraceProxyImpl.Builder<T> proxy =
+ RetraceStackTraceProxyImpl.builder(element)
+ .setRetracedClass(frame.getHolderClass())
+ .setRetracedMethod(frame)
+ .setAmbiguous(
+ frameResult.isAmbiguous() && index == 0)
+ .setTopFrame(index == 0);
+ if (element.hasLineNumber()) {
+ proxy.setLineNumber(
+ frame.getOriginalPositionOrDefault(
+ element.getLineNumber()));
+ }
+ if (element.hasFileName()) {
+ proxy.setSourceFile(
+ frameElement
+ .retraceSourceFile(frame, element.getFileName())
+ .getFilename());
+ }
+ fieldOrReturnTypeConsumer.accept(proxy);
+ argumentsConsumer.accept(proxy);
+ retracedProxies.add(proxy.build());
+ });
+ return retracedProxies.stream();
+ });
+ }));
+ }
+
+ private Stream<RetraceStackTraceProxyImpl<T>> retraceField(
+ T element, RetraceClassResult classResult) {
+ return retraceFieldOrReturnType(element)
+ .flatMap(
+ fieldOrReturnTypeConsumer ->
+ retracedMethodArguments(element)
+ .flatMap(
+ argumentsConsumer -> {
+ RetraceFieldResult retraceFieldResult =
+ classResult.lookupField(element.getFieldName());
+ return retraceFieldResult.stream()
+ .map(
+ fieldElement -> {
+ RetraceStackTraceProxyImpl.Builder<T> proxy =
+ RetraceStackTraceProxyImpl.builder(element)
+ .setRetracedClass(
+ fieldElement.getField().getHolderClass())
+ .setRetracedField(fieldElement.getField())
+ .setAmbiguous(retraceFieldResult.isAmbiguous())
+ .setTopFrame(true);
+ if (element.hasFileName()) {
+ proxy.setSourceFile(
+ fieldElement
+ .retraceSourceFile(element.getFileName())
+ .getFilename());
+ }
+ fieldOrReturnTypeConsumer.accept(proxy);
+ argumentsConsumer.accept(proxy);
+ return proxy.build();
+ });
+ }));
+ }
+
+ private Stream<Consumer<RetraceStackTraceProxyImpl.Builder<T>>> retraceFieldOrReturnType(
+ T element) {
+ if (!element.hasFieldOrReturnType()) {
+ return Stream.of(noEffect -> {});
+ }
+ String elementOrReturnType = element.getFieldOrReturnType();
+ if (elementOrReturnType.equals("void")) {
+ return Stream.of(proxy -> proxy.setRetracedFieldOrReturnType(RetracedTypeImpl.createVoid()));
+ } else {
+ TypeReference typeReference = Reference.typeFromTypeName(elementOrReturnType);
+ RetraceTypeResultImpl retraceTypeResult = retracer.retraceType(typeReference);
+ return retraceTypeResult.stream()
+ .map(
+ type ->
+ (proxy -> {
+ proxy.setRetracedFieldOrReturnType(type.getType());
+ if (retraceTypeResult.isAmbiguous()) {
+ proxy.setAmbiguous(true);
+ }
+ }));
+ }
+ }
+
+ private Stream<Consumer<RetraceStackTraceProxyImpl.Builder<T>>> retracedMethodArguments(
+ T element) {
+ if (!element.hasMethodArguments()) {
+ return Stream.of(noEffect -> {});
+ }
+ List<RetraceTypeResultImpl> retracedResults =
+ Arrays.stream(element.getMethodArguments().split(","))
+ .map(typeName -> retracer.retraceType(Reference.typeFromTypeName(typeName)))
+ .collect(Collectors.toList());
+ List<List<RetracedType>> initial = new ArrayList<>();
+ initial.add(new ArrayList<>());
+ Box<Boolean> isAmbiguous = new Box<>(false);
+ List<List<RetracedType>> retracedArguments =
+ ListUtils.fold(
+ retracedResults,
+ initial,
+ (acc, retracedTypeResult) -> {
+ if (retracedTypeResult.isAmbiguous()) {
+ isAmbiguous.set(true);
+ }
+ List<List<RetracedType>> newResult = new ArrayList<>();
+ retracedTypeResult.forEach(
+ retracedElement -> {
+ acc.forEach(
+ oldResult -> {
+ List<RetracedType> newList = new ArrayList<>(oldResult);
+ newList.add(retracedElement.getType());
+ newResult.add(newList);
+ });
+ });
+ return newResult;
+ });
+ return retracedArguments.stream()
+ .map(
+ arguments ->
+ proxy -> {
+ proxy.setRetracedMethodArguments(arguments);
+ if (isAmbiguous.get()) {
+ proxy.setAmbiguous(true);
+ }
+ });
}
public static class RetraceStackTraceProxyImpl<T extends StackTraceElementProxy<?>>
@@ -77,24 +228,36 @@
private final T originalItem;
private final RetracedClass retracedClass;
private final RetracedMethod retracedMethod;
+ private final RetracedField retracedField;
+ private final RetracedType fieldOrReturnType;
+ private final List<RetracedType> methodArguments;
private final String sourceFile;
private final int lineNumber;
private final boolean isAmbiguous;
+ private final boolean isTopFrame;
private RetraceStackTraceProxyImpl(
T originalItem,
RetracedClass retracedClass,
RetracedMethod retracedMethod,
+ RetracedField retracedField,
+ RetracedType fieldOrReturnType,
+ List<RetracedType> methodArguments,
String sourceFile,
int lineNumber,
- boolean isAmbiguous) {
+ boolean isAmbiguous,
+ boolean isTopFrame) {
assert originalItem != null;
this.originalItem = originalItem;
this.retracedClass = retracedClass;
this.retracedMethod = retracedMethod;
+ this.retracedField = retracedField;
+ this.fieldOrReturnType = fieldOrReturnType;
+ this.methodArguments = methodArguments;
this.sourceFile = sourceFile;
this.lineNumber = lineNumber;
this.isAmbiguous = isAmbiguous;
+ this.isTopFrame = isTopFrame;
}
@Override
@@ -103,6 +266,11 @@
}
@Override
+ public boolean isTopFrame() {
+ return isTopFrame;
+ }
+
+ @Override
public boolean hasRetracedClass() {
return retracedClass != null;
}
@@ -113,6 +281,11 @@
}
@Override
+ public boolean hasRetracedField() {
+ return retracedField != null;
+ }
+
+ @Override
public boolean hasSourceFile() {
return sourceFile != null;
}
@@ -123,6 +296,16 @@
}
@Override
+ public boolean hasFieldOrReturnType() {
+ return fieldOrReturnType != null;
+ }
+
+ @Override
+ public boolean hasMethodArguments() {
+ return methodArguments != null;
+ }
+
+ @Override
public T getOriginalItem() {
return originalItem;
}
@@ -138,6 +321,21 @@
}
@Override
+ public RetracedField getRetracedField() {
+ return retracedField;
+ }
+
+ @Override
+ public RetracedType getRetracedFieldOrReturnType() {
+ return fieldOrReturnType;
+ }
+
+ @Override
+ public List<RetracedType> getMethodArguments() {
+ return methodArguments;
+ }
+
+ @Override
public String getSourceFile() {
return sourceFile;
}
@@ -151,29 +349,91 @@
return lineNumber;
}
+ @Override
+ public int compareTo(RetraceStackTraceProxy<T> other) {
+ int classCompare = Boolean.compare(hasRetracedClass(), other.hasRetracedClass());
+ if (classCompare != 0) {
+ return classCompare;
+ }
+ if (hasRetracedClass()) {
+ classCompare =
+ getRetracedClass().getTypeName().compareTo(other.getRetracedClass().getTypeName());
+ if (classCompare != 0) {
+ return classCompare;
+ }
+ }
+ int methodCompare = Boolean.compare(hasRetracedMethod(), other.hasRetracedMethod());
+ if (methodCompare != 0) {
+ return methodCompare;
+ }
+ if (hasRetracedMethod()) {
+ methodCompare = getRetracedMethod().compareTo(other.getRetracedMethod());
+ if (methodCompare != 0) {
+ return methodCompare;
+ }
+ }
+ int sourceFileCompare = Boolean.compare(hasSourceFile(), other.hasSourceFile());
+ if (sourceFileCompare != 0) {
+ return sourceFileCompare;
+ }
+ if (hasSourceFile()) {
+ sourceFileCompare = getSourceFile().compareTo(other.getSourceFile());
+ if (sourceFileCompare != 0) {
+ return sourceFileCompare;
+ }
+ }
+ int lineNumberCompare = Boolean.compare(hasLineNumber(), other.hasLineNumber());
+ if (lineNumberCompare != 0) {
+ return lineNumberCompare;
+ }
+ if (hasLineNumber()) {
+ return Integer.compare(lineNumber, other.getLineNumber());
+ }
+ return 0;
+ }
+
private static class Builder<T extends StackTraceElementProxy<?>> {
private final T originalElement;
private RetracedClass classContext;
private RetracedMethod methodContext;
+ private RetracedField retracedField;
+ private RetracedType fieldOrReturnType;
+ private List<RetracedType> methodArguments;
private String sourceFile;
private int lineNumber = -1;
private boolean isAmbiguous;
+ private boolean isTopFrame;
private Builder(T originalElement) {
this.originalElement = originalElement;
}
- private Builder<T> setRetracedClassElement(RetracedClass retracedClass) {
+ private Builder<T> setRetracedClass(RetracedClass retracedClass) {
this.classContext = retracedClass;
return this;
}
- private Builder<T> setRetracedMethodElement(RetracedMethod methodElement) {
+ private Builder<T> setRetracedMethod(RetracedMethod methodElement) {
this.methodContext = methodElement;
return this;
}
+ private Builder<T> setRetracedField(RetracedField retracedField) {
+ this.retracedField = retracedField;
+ return this;
+ }
+
+ private Builder<T> setRetracedFieldOrReturnType(RetracedType retracedType) {
+ this.fieldOrReturnType = retracedType;
+ return this;
+ }
+
+ private Builder<T> setRetracedMethodArguments(List<RetracedType> arguments) {
+ this.methodArguments = arguments;
+ return this;
+ }
+
private Builder<T> setSourceFile(String sourceFile) {
this.sourceFile = sourceFile;
return this;
@@ -189,6 +449,11 @@
return this;
}
+ private Builder<T> setTopFrame(boolean topFrame) {
+ isTopFrame = topFrame;
+ return this;
+ }
+
private RetraceStackTraceProxyImpl<T> build() {
RetracedClass retracedClass = classContext;
if (methodContext != null) {
@@ -198,9 +463,13 @@
originalElement,
retracedClass,
methodContext,
- sourceFile != null ? sourceFile : null,
+ retracedField,
+ fieldOrReturnType,
+ methodArguments,
+ sourceFile,
lineNumber,
- isAmbiguous);
+ isAmbiguous,
+ isTopFrame);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
index 202d297..f649598 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
@@ -5,36 +5,53 @@
package com.android.tools.r8.retrace.internal;
import static com.android.tools.r8.retrace.internal.PlainStackTraceVisitor.firstNonWhiteSpaceCharacterFromIndex;
+import static com.android.tools.r8.retrace.internal.RetraceUtils.methodDescriptionFromRetraceMethod;
import static com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.StringIndex.noIndex;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.RetracedClass;
+import com.android.tools.r8.retrace.RetracedField;
+import com.android.tools.r8.retrace.RetracedType;
import com.android.tools.r8.retrace.StackTraceElementProxy;
import com.android.tools.r8.retrace.internal.StackTraceElementProxyRetracerImpl.RetraceStackTraceProxyImpl;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
+import com.android.tools.r8.utils.TriFunction;
import java.util.ArrayList;
import java.util.List;
-import java.util.function.BiFunction;
public final class StackTraceElementStringProxy extends StackTraceElementProxy<String> {
private final String line;
private final List<StringIndex> orderedIndices;
- private final StringIndex className;
+ private final ClassStringIndex className;
private final StringIndex methodName;
private final StringIndex sourceFile;
private final StringIndex lineNumber;
+ private final StringIndex fieldName;
+ private final StringIndex fieldOrReturnType;
+ private final StringIndex methodArguments;
private StackTraceElementStringProxy(
String line,
List<StringIndex> orderedIndices,
- StringIndex className,
+ ClassStringIndex className,
StringIndex methodName,
StringIndex sourceFile,
- StringIndex lineNumber) {
+ StringIndex lineNumber,
+ StringIndex fieldName,
+ StringIndex fieldOrReturnType,
+ StringIndex methodArguments) {
this.line = line;
this.orderedIndices = orderedIndices;
this.className = className;
this.methodName = methodName;
this.sourceFile = sourceFile;
this.lineNumber = lineNumber;
+ this.fieldName = fieldName;
+ this.fieldOrReturnType = fieldOrReturnType;
+ this.methodArguments = methodArguments;
}
static StackTraceElementStringProxyBuilder builder(String line) {
@@ -62,22 +79,37 @@
}
@Override
- public String className() {
- return hasClassName() ? getEntryInLine(className) : null;
+ public boolean hasFieldName() {
+ return fieldName.hasIndex();
}
@Override
- public String methodName() {
+ public boolean hasFieldOrReturnType() {
+ return fieldOrReturnType.hasIndex();
+ }
+
+ @Override
+ public boolean hasMethodArguments() {
+ return methodArguments.hasIndex();
+ }
+
+ @Override
+ public ClassReference getClassReference() {
+ return hasClassName() ? className.getReference(line) : null;
+ }
+
+ @Override
+ public String getMethodName() {
return hasMethodName() ? getEntryInLine(methodName) : null;
}
@Override
- public String fileName() {
+ public String getFileName() {
return hasFileName() ? getEntryInLine(sourceFile) : null;
}
@Override
- public int lineNumber() {
+ public int getLineNumber() {
if (!hasLineNumber()) {
return -1;
}
@@ -88,9 +120,25 @@
}
}
+ @Override
+ public String getFieldName() {
+ return hasFieldName() ? getEntryInLine(fieldName) : null;
+ }
+
+ @Override
+ public String getFieldOrReturnType() {
+ return hasFieldOrReturnType() ? getEntryInLine(fieldOrReturnType) : null;
+ }
+
+ @Override
+ public String getMethodArguments() {
+ return hasMethodArguments() ? getEntryInLine(methodArguments) : null;
+ }
+
public String toRetracedItem(
RetraceStackTraceProxyImpl<StackTraceElementStringProxy> retracedProxy,
- boolean printAmbiguous) {
+ boolean printAmbiguous,
+ boolean verbose) {
StringBuilder sb = new StringBuilder();
int lastSeenIndex = 0;
if (retracedProxy.isAmbiguous() && printAmbiguous) {
@@ -100,7 +148,7 @@
}
for (StringIndex index : orderedIndices) {
sb.append(line, lastSeenIndex, index.startIndex);
- sb.append(index.retracedString.apply(retracedProxy, this));
+ sb.append(index.retracedString.apply(retracedProxy, this, verbose));
lastSeenIndex = index.endIndex;
}
sb.append(line, lastSeenIndex, line.length());
@@ -116,30 +164,43 @@
return line.substring(index.startIndex, index.endIndex);
}
+ public enum ClassNameType {
+ BINARY,
+ TYPENAME
+ }
+
public static class StackTraceElementStringProxyBuilder {
private final String line;
private final List<StringIndex> orderedIndices = new ArrayList<>();
- private StringIndex className = noIndex();
+ private ClassStringIndex className = noIndex();
private StringIndex methodName = noIndex();
private StringIndex sourceFile = noIndex();
private StringIndex lineNumber = noIndex();
+ private StringIndex fieldName = noIndex();
+ private StringIndex fieldOrReturnType = noIndex();
+ private StringIndex methodArguments = noIndex();
private int lastSeenStartIndex = -1;
private StackTraceElementStringProxyBuilder(String line) {
this.line = line;
}
- public StackTraceElementStringProxyBuilder registerClassName(int startIndex, int endIndex) {
+ public StackTraceElementStringProxyBuilder registerClassName(
+ int startIndex, int endIndex, ClassNameType classNameType) {
ensureLineIndexIncreases(startIndex);
className =
- new StringIndex(
+ new ClassStringIndex(
startIndex,
endIndex,
- (retraced, original) -> {
+ (retraced, original, verbose) -> {
assert retraced.hasRetracedClass();
- return retraced.getRetracedClass().getTypeName();
- });
+ RetracedClass retracedClass = retraced.getRetracedClass();
+ return classNameType == ClassNameType.BINARY
+ ? retracedClass.getBinaryName()
+ : retracedClass.getTypeName();
+ },
+ classNameType);
orderedIndices.add(className);
return this;
}
@@ -149,10 +210,13 @@
new StringIndex(
startIndex,
endIndex,
- (retraced, original) ->
- retraced.hasRetracedMethod()
- ? retraced.getRetracedMethod().getMethodName()
- : original.methodName());
+ (retraced, original, verbose) -> {
+ if (!retraced.hasRetracedMethod()) {
+ return original.getMethodName();
+ }
+ return methodDescriptionFromRetraceMethod(
+ retraced.getRetracedMethod(), false, verbose);
+ });
orderedIndices.add(methodName);
return this;
}
@@ -162,8 +226,8 @@
new StringIndex(
startIndex,
endIndex,
- (retraced, original) ->
- retraced.hasSourceFile() ? retraced.getSourceFile() : original.fileName());
+ (retraced, original, verbose) ->
+ retraced.hasSourceFile() ? retraced.getSourceFile() : original.getFileName());
orderedIndices.add(sourceFile);
return this;
}
@@ -173,7 +237,7 @@
new StringIndex(
startIndex,
endIndex,
- (retraced, original) ->
+ (retraced, original, verbose) ->
retraced.hasLineNumber()
? retraced.getLineNumber() + ""
: original.lineNumberAsString());
@@ -181,9 +245,73 @@
return this;
}
+ public StackTraceElementStringProxyBuilder registerFieldName(int startIndex, int endIndex) {
+ fieldName =
+ new StringIndex(
+ startIndex,
+ endIndex,
+ (retraced, original, verbose) -> {
+ if (!retraced.hasRetracedField()) {
+ return original.getFieldName();
+ }
+ RetracedField retracedField = retraced.getRetracedField();
+ if (!verbose || retracedField.isUnknown()) {
+ return retracedField.getFieldName();
+ }
+ return retracedField.asKnown().getFieldType().getTypeName()
+ + " "
+ + retracedField.getFieldName();
+ });
+ orderedIndices.add(fieldName);
+ return this;
+ }
+
+ public StackTraceElementStringProxyBuilder registerFieldOrReturnType(
+ int startIndex, int endIndex) {
+ fieldOrReturnType =
+ new StringIndex(
+ startIndex,
+ endIndex,
+ (retraced, original, verbose) -> {
+ if (!retraced.hasFieldOrReturnType()) {
+ return original.getFieldOrReturnType();
+ }
+ return retraced.getRetracedFieldOrReturnType().isVoid()
+ ? "void"
+ : retraced.getRetracedFieldOrReturnType().getTypeName();
+ });
+ orderedIndices.add(fieldOrReturnType);
+ return this;
+ }
+
+ public StackTraceElementStringProxyBuilder registerMethodArguments(
+ int startIndex, int endIndex) {
+ methodArguments =
+ new StringIndex(
+ startIndex,
+ endIndex,
+ (retraced, original, verbose) -> {
+ if (!retraced.hasMethodArguments()) {
+ return original.getMethodArguments();
+ }
+ return StringUtils.join(
+ retraced.getMethodArguments(), ",", BraceType.NONE, RetracedType::getTypeName);
+ });
+ orderedIndices.add(methodArguments);
+ return this;
+ }
+
public StackTraceElementStringProxy build() {
return new StackTraceElementStringProxy(
- line, orderedIndices, className, methodName, sourceFile, lineNumber);
+ line,
+ orderedIndices,
+ className,
+ methodName,
+ sourceFile,
+ lineNumber,
+ fieldName,
+ fieldOrReturnType,
+ methodArguments);
}
private void ensureLineIndexIncreases(int newStartIndex) {
@@ -194,28 +322,31 @@
}
}
- static final class StringIndex {
+ static class StringIndex {
- private static final StringIndex NO_INDEX = new StringIndex(-1, -1, null);
+ private static final ClassStringIndex NO_INDEX =
+ new ClassStringIndex(-1, -1, null, ClassNameType.TYPENAME);
- static StringIndex noIndex() {
+ static ClassStringIndex noIndex() {
return NO_INDEX;
}
- private final int startIndex;
- private final int endIndex;
- private final BiFunction<
+ protected final int startIndex;
+ protected final int endIndex;
+ private final TriFunction<
RetraceStackTraceProxyImpl<StackTraceElementStringProxy>,
StackTraceElementStringProxy,
+ Boolean,
String>
retracedString;
private StringIndex(
int startIndex,
int endIndex,
- BiFunction<
+ TriFunction<
RetraceStackTraceProxyImpl<StackTraceElementStringProxy>,
StackTraceElementStringProxy,
+ Boolean,
String>
retracedString) {
this.startIndex = startIndex;
@@ -227,4 +358,30 @@
return this != NO_INDEX;
}
}
+
+ static final class ClassStringIndex extends StringIndex {
+
+ private final ClassNameType classNameType;
+
+ private ClassStringIndex(
+ int startIndex,
+ int endIndex,
+ TriFunction<
+ RetraceStackTraceProxyImpl<StackTraceElementStringProxy>,
+ StackTraceElementStringProxy,
+ Boolean,
+ String>
+ retracedString,
+ ClassNameType classNameType) {
+ super(startIndex, endIndex, retracedString);
+ this.classNameType = classNameType;
+ }
+
+ ClassReference getReference(String line) {
+ String className = line.substring(startIndex, endIndex);
+ return classNameType == ClassNameType.BINARY
+ ? Reference.classFromBinaryName(className)
+ : Reference.classFromTypeName(className);
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index 46e4629..fc6cb56 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import java.util.IdentityHashMap;
@@ -27,6 +28,7 @@
public class AnnotationRemover {
private final AppView<AppInfoWithLiveness> appView;
+ private final InternalOptions options;
private final Set<DexAnnotation> annotationsToRetain;
private final Set<DexType> classesToRetainInnerClassAttributeFor;
private final ProguardKeepAttributes keep;
@@ -38,6 +40,7 @@
Set<DexAnnotation> annotationsToRetain,
Set<DexType> removedClasses) {
this.appView = appView;
+ this.options = appView.options();
this.annotationsToRetain = annotationsToRetain;
this.classesToRetainInnerClassAttributeFor = classesToRetainInnerClassAttributeFor;
this.keep = appView.options().getProguardConfiguration().getKeepAttributes();
@@ -187,25 +190,31 @@
stripAttributes(clazz);
clazz.setAnnotations(
clazz.annotations().rewrite(annotation -> rewriteAnnotation(clazz, annotation)));
- clazz.forEachMethod(this::processMethod);
- clazz.forEachField(this::processField);
+ clazz.forEachMethod(method -> processMethod(method, clazz));
+ clazz.forEachField(field -> processField(field, clazz));
}
}
- private void processMethod(DexEncodedMethod method) {
+ private void processMethod(DexEncodedMethod method, DexProgramClass clazz) {
method.setAnnotations(
method.annotations().rewrite(annotation -> rewriteAnnotation(method, annotation)));
method.parameterAnnotationsList =
method.parameterAnnotationsList.keepIf(this::filterParameterAnnotations);
- if (!keep.signature) {
+ if (appView
+ .getKeepInfo()
+ .getMethodInfo(method, clazz)
+ .isAllowSignatureAttributeRemovalAllowed(options)) {
method.clearGenericSignature();
}
}
- private void processField(DexEncodedField field) {
+ private void processField(DexEncodedField field, DexProgramClass clazz) {
field.setAnnotations(
field.annotations().rewrite(annotation -> rewriteAnnotation(field, annotation)));
- if (!keep.signature) {
+ if (appView
+ .getKeepInfo()
+ .getFieldInfo(field, clazz)
+ .isAllowSignatureAttributeRemovalAllowed(options)) {
field.clearGenericSignature();
}
}
@@ -321,8 +330,10 @@
clazz.clearEnclosingMethodAttribute();
clazz.clearInnerClasses();
}
- // TODO(b/170077516): Prune attributes.
- if (!keep.signature) {
+ if (appView
+ .getKeepInfo()
+ .getClassInfo(clazz)
+ .isAllowSignatureAttributeRemovalAllowed(options)) {
clazz.clearClassSignature();
}
}
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 7c6d911..b54a0db 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMember;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexReference;
@@ -136,9 +137,9 @@
/** All items with assumemayhavesideeffects rule. */
public final Map<DexReference, ProguardMemberRule> mayHaveSideEffects;
/** All items with assumenosideeffects rule. */
- public final Map<DexReference, ProguardMemberRule> noSideEffects;
+ public final Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects;
/** All items with assumevalues rule. */
- public final Map<DexReference, ProguardMemberRule> assumedValues;
+ public final Map<DexMember<?, ?>, ProguardMemberRule> assumedValues;
/** All methods that should be inlined if possible due to a configuration directive. */
public final Set<DexMethod> alwaysInline;
/** All methods that *must* be inlined due to a configuration directive (testing only). */
@@ -213,8 +214,8 @@
Map<DexCallSite, ProgramMethodSet> callSites,
KeepInfoCollection keepInfo,
Map<DexReference, ProguardMemberRule> mayHaveSideEffects,
- Map<DexReference, ProguardMemberRule> noSideEffects,
- Map<DexReference, ProguardMemberRule> assumedValues,
+ Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects,
+ Map<DexMember<?, ?>, ProguardMemberRule> assumedValues,
Set<DexMethod> alwaysInline,
Set<DexMethod> forceInline,
Set<DexMethod> neverInline,
@@ -294,8 +295,8 @@
Map<DexCallSite, ProgramMethodSet> callSites,
KeepInfoCollection keepInfo,
Map<DexReference, ProguardMemberRule> mayHaveSideEffects,
- Map<DexReference, ProguardMemberRule> noSideEffects,
- Map<DexReference, ProguardMemberRule> assumedValues,
+ Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects,
+ Map<DexMember<?, ?>, ProguardMemberRule> assumedValues,
Set<DexMethod> alwaysInline,
Set<DexMethod> forceInline,
Set<DexMethod> neverInline,
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index a7fdc85..be79780 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -57,6 +57,7 @@
import com.android.tools.r8.graph.PresortedComparable;
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMember;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.ResolutionResult.FailedResolutionResult;
@@ -93,6 +94,7 @@
import com.android.tools.r8.shaking.KeepInfoCollection.MutableKeepInfoCollection;
import com.android.tools.r8.shaking.RootSetBuilder.ConsequentRootSet;
import com.android.tools.r8.shaking.RootSetBuilder.ItemsWithRules;
+import com.android.tools.r8.shaking.RootSetBuilder.MutableItemsWithRules;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.shaking.ScopedDexMethodSet.AddMethodIfMoreVisibleResult;
import com.android.tools.r8.utils.Action;
@@ -126,6 +128,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -2125,9 +2128,10 @@
worklist.addIfNotSeen(interfaces);
// First we lookup and mark all targets on the instantiated class for each reachable method in
// the super chain (inclusive).
+ DexClass initialClass = clazz;
while (clazz != null) {
if (clazz.isProgramClass()) {
- markProgramMethodOverridesAsLive(instantiation, clazz.asProgramClass(), seen);
+ markProgramMethodOverridesAsLive(instantiation, initialClass, clazz.asProgramClass(), seen);
} else {
markLibraryAndClasspathMethodOverridesAsLive(instantiation, clazz);
}
@@ -2148,7 +2152,7 @@
if (iface.isNotProgramClass()) {
markLibraryAndClasspathMethodOverridesAsLive(instantiation, iface);
} else {
- markProgramMethodOverridesAsLive(instantiation, iface.asProgramClass(), seen);
+ markProgramMethodOverridesAsLive(instantiation, initialClass, iface.asProgramClass(), seen);
}
worklist.addIfNotSeen(Arrays.asList(iface.interfaces.values));
}
@@ -2160,18 +2164,28 @@
private void markProgramMethodOverridesAsLive(
InstantiatedObject instantiation,
+ DexClass initialClass,
DexProgramClass superClass,
Set<Wrapper<DexMethod>> seenMethods) {
for (DexMethod method : getReachableVirtualTargets(superClass)) {
assert method.holder == superClass.type;
- if (seenMethods.add(MethodSignatureEquivalence.get().wrap(method))) {
+ Wrapper<DexMethod> signature = MethodSignatureEquivalence.get().wrap(method);
+ if (!seenMethods.contains(signature)) {
SingleResolutionResult resolution =
appInfo.resolveMethodOn(superClass, method).asSingleResolution();
assert resolution != null;
assert resolution.getResolvedHolder().isProgramClass();
- if (resolution != null && resolution.getResolvedHolder().isProgramClass()) {
- markLiveOverrides(
- instantiation, superClass, resolution.getResolutionPair().asProgramMethod());
+ if (resolution != null) {
+ if (!initialClass.isProgramClass()
+ || resolution
+ .isAccessibleForVirtualDispatchFrom(initialClass.asProgramClass(), appInfo)
+ .isTrue()) {
+ seenMethods.add(signature);
+ }
+ if (resolution.getResolvedHolder().isProgramClass()) {
+ markLiveOverrides(
+ instantiation, superClass, resolution.getResolutionPair().asProgramMethod());
+ }
}
}
}
@@ -2370,6 +2384,8 @@
// Add all dependent members to the workqueue.
enqueueRootItems(rootSet.getDependentItems(field.getDefinition()));
+ checkMemberForSoftPinning(field);
+
// Notify analyses.
analyses.forEach(analysis -> analysis.processNewlyLiveField(field));
}
@@ -2386,6 +2402,8 @@
// Add all dependent members to the workqueue.
enqueueRootItems(rootSet.getDependentItems(field.getDefinition()));
+ checkMemberForSoftPinning(field);
+
// Notify analyses.
analyses.forEach(analysis -> analysis.processNewlyLiveField(field));
}
@@ -3400,12 +3418,26 @@
enqueueRootItems(dependentItems);
}
});
+ consequentRootSet.dependentSoftPinned.forEach(
+ (reference, dependentItems) -> {
+ if (isLiveProgramReference(reference)) {
+ dependentItems.forEachReference(
+ item -> {
+ if (isLiveProgramReference(item)) {
+ keepInfo.joinInfo(item, appView, Joiner::pin);
+ }
+ });
+ }
+ });
+
// TODO(b/132600955): This modifies the root set. Should the consequent be persistent?
rootSet.addConsequentRootSet(consequentRootSet, addNoShrinking);
if (mode.isInitialTreeShaking()) {
for (DexReference reference : consequentRootSet.noObfuscation) {
keepInfo.evaluateRule(reference, appView, Joiner::disallowMinification);
}
+ consequentRootSet.softPinned.forEachReference(
+ reference -> keepInfo.evaluateRule(reference, appView, Joiner::pin));
}
enqueueRootItems(consequentRootSet.noShrinking);
// Check for compatibility rules indicating that the holder must be implicitly kept.
@@ -3419,6 +3451,18 @@
}
}
+ private boolean isLiveProgramReference(DexReference reference) {
+ if (reference.isDexType()) {
+ DexProgramClass clazz =
+ DexProgramClass.asProgramClassOrNull(definitionFor(reference.asDexType()));
+ return clazz != null && isTypeLive(clazz);
+ }
+ DexMember<?, ?> member = reference.asDexMember();
+ DexProgramClass holder = DexProgramClass.asProgramClassOrNull(definitionFor(member.holder));
+ ProgramMember<?, ?> programMember = member.lookupOnProgramClass(holder);
+ return programMember != null && isMemberLive(programMember.getDefinition());
+ }
+
private ConsequentRootSet computeDelayedInterfaceMethodSyntheticBridges() {
RootSetBuilder builder = new RootSetBuilder(appView, subtypingInfo);
for (DelayedRootSetActionItem delayedRootSetActionItem : rootSet.delayedRootSetActionItems) {
@@ -3430,7 +3474,8 @@
return builder.buildConsequentRootSet();
}
- private Map<DexMethod, ProgramMethod> syntheticInterfaceMethodBridges = new IdentityHashMap<>();
+ private final Map<DexMethod, ProgramMethod> syntheticInterfaceMethodBridges =
+ new LinkedHashMap<>();
private void handleInterfaceMethodSyntheticBridgeAction(
InterfaceMethodSyntheticBridgeAction action, RootSetBuilder builder) {
@@ -3608,10 +3653,28 @@
// Add all dependent members to the workqueue.
enqueueRootItems(rootSet.getDependentItems(definition));
+ checkMemberForSoftPinning(method);
+
// Notify analyses.
analyses.forEach(analysis -> analysis.processNewlyLiveMethod(method));
}
+ private void checkMemberForSoftPinning(ProgramMember<?, ?> member) {
+ DexMember<?, ?> reference = member.getDefinition().toReference();
+ Set<ProguardKeepRuleBase> softPinRules = rootSet.softPinned.getRulesForReference(reference);
+ if (softPinRules != null) {
+ assert softPinRules.stream().noneMatch(r -> r.getModifiers().allowsOptimization);
+ keepInfo.joinInfo(reference, appInfo, Joiner::pin);
+ }
+ // Identify dependent soft pinning.
+ MutableItemsWithRules items = rootSet.dependentSoftPinned.get(member.getHolderType());
+ if (items != null && items.containsReference(reference)) {
+ assert items.getRulesForReference(reference).stream()
+ .noneMatch(r -> r.getModifiers().allowsOptimization);
+ keepInfo.joinInfo(reference, appInfo, Joiner::pin);
+ }
+ }
+
private void markReferencedTypesAsLive(ProgramMethod method) {
markTypeAsLive(
method.getHolderType(), clazz -> graphReporter.reportClassReferencedFrom(clazz, method));
diff --git a/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java b/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java
index c6a1725..2de5c70 100644
--- a/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java
+++ b/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java
@@ -6,6 +6,7 @@
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.DexProgramClass;
import com.android.tools.r8.graph.DexType;
@@ -222,13 +223,13 @@
}
}
- DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, context);
+ DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context);
if (singleTarget == null) {
return false;
}
InstanceInitializerInfo initializerInfo =
- singleTarget.getOptimizationInfo().getInstanceInitializerInfo();
+ singleTarget.getDefinition().getOptimizationInfo().getInstanceInitializerInfo();
return initializerInfo.receiverNeverEscapesOutsideConstructorChain();
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index f8b5987..a5ee8b7 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -5,8 +5,8 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.AssumeNoSideEffectsRuleForObjectMembersDiagnostic;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
@@ -67,6 +67,7 @@
import java.util.Queue;
import java.util.Set;
import java.util.Stack;
+import java.util.TreeSet;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -83,6 +84,7 @@
private final DirectMappedDexApplication application;
private final Iterable<? extends ProguardConfigurationRule> rules;
private final MutableItemsWithRules noShrinking = new MutableItemsWithRules();
+ private final MutableItemsWithRules softPinned = new MutableItemsWithRules();
private final Set<DexReference> noObfuscation = Sets.newIdentityHashSet();
private final LinkedHashMap<DexReference, DexReference> reasonAsked = new LinkedHashMap<>();
private final LinkedHashMap<DexReference, DexReference> checkDiscarded = new LinkedHashMap<>();
@@ -103,11 +105,13 @@
private final Set<DexReference> neverPropagateValue = Sets.newIdentityHashSet();
private final Map<DexReference, MutableItemsWithRules> dependentNoShrinking =
new IdentityHashMap<>();
+ private final Map<DexReference, MutableItemsWithRules> dependentSoftPinned =
+ new IdentityHashMap<>();
private final Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule =
new IdentityHashMap<>();
private final Map<DexReference, ProguardMemberRule> mayHaveSideEffects = new IdentityHashMap<>();
- private final Map<DexReference, ProguardMemberRule> noSideEffects = new IdentityHashMap<>();
- private final Map<DexReference, ProguardMemberRule> assumedValues = new IdentityHashMap<>();
+ private final Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects = new IdentityHashMap<>();
+ private final Map<DexMember<?, ?>, ProguardMemberRule> assumedValues = new IdentityHashMap<>();
private final Set<DexReference> identifierNameStrings = Sets.newIdentityHashSet();
private final Queue<DelayedRootSetActionItem> delayedRootSetActionItems =
new ConcurrentLinkedQueue<>();
@@ -116,8 +120,8 @@
private final DexStringCache dexStringCache = new DexStringCache();
private final Set<ProguardIfRule> ifRules = Sets.newIdentityHashSet();
- private final Map<OriginWithPosition, List<DexMethod>> assumeNoSideEffectsWarnings =
- new HashMap<>();
+ private final Map<OriginWithPosition, Set<DexMethod>> assumeNoSideEffectsWarnings =
+ new LinkedHashMap<>();
public RootSetBuilder(
AppView<? extends AppInfoWithClassHierarchy> appView,
@@ -334,6 +338,7 @@
assert appView.options().isMinificationEnabled() || noObfuscation.isEmpty();
return new RootSet(
noShrinking,
+ softPinned,
noObfuscation,
ImmutableList.copyOf(reasonAsked.values()),
ImmutableList.copyOf(checkDiscarded.values()),
@@ -356,6 +361,7 @@
noSideEffects,
assumedValues,
dependentNoShrinking,
+ dependentSoftPinned,
dependentKeepClassCompatRule,
identifierNameStrings,
ifRules,
@@ -382,7 +388,7 @@
DexType type,
DexMethod reference,
Set<DexType> subTypes,
- Map<DexReference, ProguardMemberRule> assumeRulePool) {
+ Map<DexMember<?, ?>, ProguardMemberRule> assumeRulePool) {
ProguardMemberRule ruleToBePropagated = null;
for (DexType subType : subTypes) {
DexMethod referenceInSubType =
@@ -424,8 +430,10 @@
neverInline,
neverClassInline,
noShrinking,
+ softPinned,
noObfuscation,
dependentNoShrinking,
+ dependentSoftPinned,
dependentKeepClassCompatRule,
Lists.newArrayList(delayedRootSetActionItems));
}
@@ -1136,6 +1144,14 @@
noShrinking.addReferenceWithRule(item.toReference(), keepRule);
}
context.markAsUsed();
+ } else if (!modifiers.allowsOptimization) {
+ if (precondition != null) {
+ dependentSoftPinned
+ .computeIfAbsent(precondition.toReference(), x -> new MutableItemsWithRules())
+ .addReferenceWithRule(item.toReference(), keepRule);
+ } else {
+ softPinned.addReferenceWithRule(item.toReference(), keepRule);
+ }
}
if (!modifiers.allowsOptimization) {
// The -dontoptimize flag has only effect through the keep all rule, but we still
@@ -1155,15 +1171,25 @@
mayHaveSideEffects.put(item.toReference(), rule);
context.markAsUsed();
} else if (context instanceof ProguardAssumeNoSideEffectRule) {
- checkAssumeNoSideEffectsWarnings(item, (ProguardAssumeNoSideEffectRule) context, rule);
- noSideEffects.put(item.toReference(), rule);
- context.markAsUsed();
+ if (item.isDexEncodedMember()) {
+ DexEncodedMember<?, ?> member = item.asDexEncodedMember();
+ if (member.holder() == appView.dexItemFactory().objectType) {
+ assert member.isDexEncodedMethod();
+ reportAssumeNoSideEffectsWarningForJavaLangClassMethod(
+ member.asDexEncodedMethod(), (ProguardAssumeNoSideEffectRule) context);
+ } else {
+ noSideEffects.put(member.toReference(), rule);
+ }
+ context.markAsUsed();
+ }
} else if (context instanceof ProguardWhyAreYouKeepingRule) {
reasonAsked.computeIfAbsent(item.toReference(), i -> i);
context.markAsUsed();
} else if (context instanceof ProguardAssumeValuesRule) {
- assumedValues.put(item.toReference(), rule);
- context.markAsUsed();
+ if (item.isDexEncodedMember()) {
+ assumedValues.put(item.asDexEncodedMember().toReference(), rule);
+ context.markAsUsed();
+ }
} else if (context instanceof ProguardCheckDiscardRule) {
checkDiscarded.computeIfAbsent(item.toReference(), i -> i);
context.markAsUsed();
@@ -1301,8 +1327,10 @@
final Set<DexMethod> neverInline;
final Set<DexType> neverClassInline;
final MutableItemsWithRules noShrinking;
+ final MutableItemsWithRules softPinned;
final Set<DexReference> noObfuscation;
final Map<DexReference, MutableItemsWithRules> dependentNoShrinking;
+ final Map<DexReference, MutableItemsWithRules> dependentSoftPinned;
final Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule;
final List<DelayedRootSetActionItem> delayedRootSetActionItems;
@@ -1310,15 +1338,19 @@
Set<DexMethod> neverInline,
Set<DexType> neverClassInline,
MutableItemsWithRules noShrinking,
+ MutableItemsWithRules softPinned,
Set<DexReference> noObfuscation,
Map<DexReference, MutableItemsWithRules> dependentNoShrinking,
+ Map<DexReference, MutableItemsWithRules> dependentSoftPinned,
Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
List<DelayedRootSetActionItem> delayedRootSetActionItems) {
this.neverInline = neverInline;
this.neverClassInline = neverClassInline;
this.noShrinking = noShrinking;
+ this.softPinned = softPinned;
this.noObfuscation = noObfuscation;
this.dependentNoShrinking = dependentNoShrinking;
+ this.dependentSoftPinned = dependentSoftPinned;
this.dependentKeepClassCompatRule = dependentKeepClassCompatRule;
this.delayedRootSetActionItems = delayedRootSetActionItems;
}
@@ -1445,19 +1477,20 @@
return reference.apply(this::containsClass, this::containsField, this::containsMethod);
}
- public abstract void forEachClass(Consumer<DexType> consumer);
+ public abstract void forEachClass(Consumer<? super DexType> consumer);
- public abstract void forEachClass(BiConsumer<DexType, Set<ProguardKeepRuleBase>> consumer);
+ public abstract void forEachClass(
+ BiConsumer<? super DexType, Set<ProguardKeepRuleBase>> consumer);
public abstract void forEachField(Consumer<? super DexField> consumer);
public abstract void forEachField(
BiConsumer<? super DexField, Set<ProguardKeepRuleBase>> consumer);
- public abstract void forEachMember(Consumer<DexMember<?, ?>> consumer);
+ public abstract void forEachMember(Consumer<? super DexMember<?, ?>> consumer);
public abstract void forEachMember(
- BiConsumer<DexMember<?, ?>, Set<ProguardKeepRuleBase>> consumer);
+ BiConsumer<? super DexMember<?, ?>, Set<ProguardKeepRuleBase>> consumer);
public abstract void forEachMethod(Consumer<? super DexMethod> consumer);
@@ -1554,13 +1587,18 @@
return methodsWithRules.containsKey(method);
}
+ public void forEachReference(Consumer<DexReference> consumer) {
+ forEachClass(consumer);
+ forEachMember(consumer);
+ }
+
@Override
- public void forEachClass(Consumer<DexType> consumer) {
+ public void forEachClass(Consumer<? super DexType> consumer) {
classesWithRules.keySet().forEach(consumer);
}
@Override
- public void forEachClass(BiConsumer<DexType, Set<ProguardKeepRuleBase>> consumer) {
+ public void forEachClass(BiConsumer<? super DexType, Set<ProguardKeepRuleBase>> consumer) {
classesWithRules.forEach(consumer);
}
@@ -1575,13 +1613,14 @@
}
@Override
- public void forEachMember(Consumer<DexMember<?, ?>> consumer) {
+ public void forEachMember(Consumer<? super DexMember<?, ?>> consumer) {
forEachField(consumer);
forEachMethod(consumer);
}
@Override
- public void forEachMember(BiConsumer<DexMember<?, ?>, Set<ProguardKeepRuleBase>> consumer) {
+ public void forEachMember(
+ BiConsumer<? super DexMember<?, ?>, Set<ProguardKeepRuleBase>> consumer) {
forEachField(consumer);
forEachMethod(consumer);
}
@@ -1655,18 +1694,13 @@
}
}
- private void checkAssumeNoSideEffectsWarnings(
- DexDefinition item, ProguardAssumeNoSideEffectRule context, ProguardMemberRule rule) {
- if (rule.getRuleType() == ProguardMemberType.METHOD && rule.isSpecific()) {
- return;
- }
- if (item.isDexEncodedMethod()) {
- DexEncodedMethod method = item.asDexEncodedMethod();
- if (method.holder() == options.itemFactory.objectType) {
- OriginWithPosition key = new OriginWithPosition(context.getOrigin(), context.getPosition());
- assumeNoSideEffectsWarnings.computeIfAbsent(key, k -> new ArrayList<>()).add(method.method);
- }
- }
+ private void reportAssumeNoSideEffectsWarningForJavaLangClassMethod(
+ DexEncodedMethod method, ProguardAssumeNoSideEffectRule context) {
+ assert method.getHolderType() == options.dexItemFactory().objectType;
+ OriginWithPosition key = new OriginWithPosition(context.getOrigin(), context.getPosition());
+ assumeNoSideEffectsWarnings
+ .computeIfAbsent(key, ignore -> new TreeSet<>(DexMethod::slowCompareTo))
+ .add(method.getReference());
}
private boolean isWaitOrNotifyMethod(DexMethod method) {
@@ -1680,50 +1714,27 @@
options.getProguardConfiguration() != null
? options.getProguardConfiguration().getDontWarnPatterns()
: ProguardClassFilter.empty();
+ if (dontWarnPatterns.matches(options.itemFactory.objectType)) {
+ // Don't report any warnings since we don't apply -assumenosideeffects rules to notify() or
+ // wait() anyway.
+ return;
+ }
assumeNoSideEffectsWarnings.forEach(
(originWithPosition, methods) -> {
- boolean waitOrNotifyMethods = methods.stream().anyMatch(this::isWaitOrNotifyMethod);
- boolean dontWarnObject = dontWarnPatterns.matches(options.itemFactory.objectType);
- StringBuilder message = new StringBuilder();
- message.append(
- "The -assumenosideeffects rule matches methods on `java.lang.Object` with wildcards");
- message.append(" including the method(s) ");
- for (int i = 0; i < methods.size(); i++) {
- if (i > 0) {
- message.append(i < methods.size() - 1 ? ", " : " and ");
- }
- message.append("`");
- message.append(methods.get(i).toSourceStringWithoutHolder());
- message.append("`.");
+ boolean matchesWaitOrNotifyMethods =
+ methods.stream().anyMatch(this::isWaitOrNotifyMethod);
+ if (!matchesWaitOrNotifyMethods) {
+ // We model the remaining methods on java.lang.Object, and thus there should be no need
+ // to warn in this case.
+ return;
}
- if (waitOrNotifyMethods) {
- message.append(" This will most likely cause problems.");
- } else {
- message.append(" This is most likely not intended.");
- }
- if (waitOrNotifyMethods && !dontWarnObject) {
- message.append(" Specify the methods more precisely.");
- } else {
- message.append(" Consider specifying the methods more precisely.");
- }
- Diagnostic diagnostic =
- new StringDiagnostic(
- message.toString(),
- originWithPosition.getOrigin(),
- originWithPosition.getPosition());
- if (waitOrNotifyMethods) {
- if (!dontWarnObject) {
- options.reporter.error(diagnostic);
- } else {
- options.reporter.warning(diagnostic);
- }
-
- } else {
- if (!dontWarnObject) {
- options.reporter.warning(diagnostic);
- }
- }
+ options.reporter.warning(
+ new AssumeNoSideEffectsRuleForObjectMembersDiagnostic.Builder()
+ .addMatchedMethods(methods)
+ .setOrigin(originWithPosition.getOrigin())
+ .setPosition(originWithPosition.getPosition())
+ .build());
});
}
@@ -1745,13 +1756,14 @@
public final Set<DexType> noStaticClassMerging;
public final Set<DexReference> neverPropagateValue;
public final Map<DexReference, ProguardMemberRule> mayHaveSideEffects;
- public final Map<DexReference, ProguardMemberRule> noSideEffects;
- public final Map<DexReference, ProguardMemberRule> assumedValues;
+ public final Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects;
+ public final Map<DexMember<?, ?>, ProguardMemberRule> assumedValues;
public final Set<DexReference> identifierNameStrings;
public final Set<ProguardIfRule> ifRules;
private RootSet(
MutableItemsWithRules noShrinking,
+ MutableItemsWithRules softPinned,
Set<DexReference> noObfuscation,
ImmutableList<DexReference> reasonAsked,
ImmutableList<DexReference> checkDiscarded,
@@ -1771,9 +1783,10 @@
Set<DexType> noStaticClassMerging,
Set<DexReference> neverPropagateValue,
Map<DexReference, ProguardMemberRule> mayHaveSideEffects,
- Map<DexReference, ProguardMemberRule> noSideEffects,
- Map<DexReference, ProguardMemberRule> assumedValues,
+ Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects,
+ Map<DexMember<?, ?>, ProguardMemberRule> assumedValues,
Map<DexReference, MutableItemsWithRules> dependentNoShrinking,
+ Map<DexReference, MutableItemsWithRules> dependentSoftPinned,
Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
Set<DexReference> identifierNameStrings,
Set<ProguardIfRule> ifRules,
@@ -1782,8 +1795,10 @@
neverInline,
neverClassInline,
noShrinking,
+ softPinned,
noObfuscation,
dependentNoShrinking,
+ dependentSoftPinned,
dependentKeepClassCompatRule,
delayedRootSetActionItems);
this.reasonAsked = reasonAsked;
@@ -1831,7 +1846,8 @@
if (addNoShrinking) {
noShrinking.addAll(consequentRootSet.noShrinking);
}
- addDependentItems(consequentRootSet.dependentNoShrinking);
+ addDependentItems(consequentRootSet.dependentNoShrinking, dependentNoShrinking);
+ addDependentItems(consequentRootSet.dependentSoftPinned, dependentSoftPinned);
consequentRootSet.dependentKeepClassCompatRule.forEach(
(type, rules) ->
dependentKeepClassCompatRule.computeIfAbsent(
@@ -1840,10 +1856,12 @@
}
// Add dependent items that depend on -if rules.
- private void addDependentItems(Map<DexReference, ? extends ItemsWithRules> dependentItems) {
- dependentItems.forEach(
+ private static void addDependentItems(
+ Map<DexReference, ? extends ItemsWithRules> dependentItemsToAdd,
+ Map<DexReference, MutableItemsWithRules> dependentItemsToAddTo) {
+ dependentItemsToAdd.forEach(
(reference, dependence) ->
- dependentNoShrinking
+ dependentItemsToAddTo
.computeIfAbsent(reference, x -> new MutableItemsWithRules())
.putAll(dependence));
}
@@ -1855,11 +1873,15 @@
if (noObfuscation.contains(original)) {
noObfuscation.add(rewritten);
}
- if (noSideEffects.containsKey(original)) {
- noSideEffects.put(rewritten, noSideEffects.get(original));
- }
- if (assumedValues.containsKey(original)) {
- assumedValues.put(rewritten, assumedValues.get(original));
+ if (original.isDexMember()) {
+ assert rewritten.isDexMember();
+ DexMember<?, ?> originalMember = original.asDexMember();
+ if (noSideEffects.containsKey(originalMember)) {
+ noSideEffects.put(rewritten.asDexMember(), noSideEffects.get(originalMember));
+ }
+ if (assumedValues.containsKey(originalMember)) {
+ assumedValues.put(rewritten.asDexMember(), assumedValues.get(originalMember));
+ }
}
}
@@ -2075,16 +2097,20 @@
Set<DexMethod> neverInline,
Set<DexType> neverClassInline,
MutableItemsWithRules noShrinking,
+ MutableItemsWithRules softPinned,
Set<DexReference> noObfuscation,
Map<DexReference, MutableItemsWithRules> dependentNoShrinking,
+ Map<DexReference, MutableItemsWithRules> dependentSoftPinned,
Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
List<DelayedRootSetActionItem> delayedRootSetActionItems) {
super(
neverInline,
neverClassInline,
noShrinking,
+ softPinned,
noObfuscation,
dependentNoShrinking,
+ dependentSoftPinned,
dependentKeepClassCompatRule,
delayedRootSetActionItems);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 6100000..de95f92 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -241,8 +241,8 @@
initializeMergeCandidates(classes);
}
- public VerticallyMergedClasses getMergedClasses() {
- return new VerticallyMergedClasses(mergedClasses);
+ private VerticallyMergedClasses getMergedClasses() {
+ return new VerticallyMergedClasses(mergedClasses, mergedClassesInverse);
}
private void initializeMergeCandidates(Iterable<DexProgramClass> classes) {
@@ -1484,7 +1484,7 @@
for (SynthesizedBridgeCode synthesizedBridge : synthesizedBridges) {
synthesizedBridge.updateMethodSignatures(this::fixupMethod);
}
- VerticalClassMergerGraphLens lens = lensBuilder.build(appView, mergedClasses);
+ VerticalClassMergerGraphLens lens = lensBuilder.build(appView, getMergedClasses());
if (lens != null) {
new AnnotationFixer(lens).run(appView.appInfo().classes());
}
@@ -1728,6 +1728,11 @@
}
@Override
+ public Iterable<DexType> getOriginalTypes(DexType type) {
+ throw new Unreachable();
+ }
+
+ @Override
public DexField getOriginalFieldSignature(DexField field) {
throw new Unreachable();
}
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
index a95cfb4..a5481fd 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
@@ -14,10 +14,14 @@
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.utils.IterableUtils;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
@@ -55,6 +59,7 @@
private final AppView<?> appView;
+ private VerticallyMergedClasses mergedClasses;
private final Map<DexType, Map<DexMethod, GraphLensLookupResultProvider>>
contextualVirtualToDirectMethodMaps;
private Set<DexMethod> mergedMethods;
@@ -62,7 +67,7 @@
private VerticalClassMergerGraphLens(
AppView<?> appView,
- Map<DexType, DexType> typeMap,
+ VerticallyMergedClasses mergedClasses,
Map<DexField, DexField> fieldMap,
Map<DexMethod, DexMethod> methodMap,
Set<DexMethod> mergedMethods,
@@ -73,7 +78,7 @@
Map<DexMethod, DexMethod> originalMethodSignaturesForBridges,
GraphLens previousLens) {
super(
- typeMap,
+ mergedClasses.getForwardMap(),
methodMap,
fieldMap,
originalFieldSignatures,
@@ -81,14 +86,24 @@
previousLens,
appView.dexItemFactory());
this.appView = appView;
+ this.mergedClasses = mergedClasses;
this.contextualVirtualToDirectMethodMaps = contextualVirtualToDirectMethodMaps;
this.mergedMethods = mergedMethods;
this.originalMethodSignaturesForBridges = originalMethodSignaturesForBridges;
}
+ public VerticallyMergedClasses getMergedClasses() {
+ return mergedClasses;
+ }
+
@Override
- public DexType getOriginalType(DexType type) {
- return getPrevious().getOriginalType(type);
+ protected Iterable<DexType> internalGetOriginalTypes(DexType previous) {
+ Collection<DexType> originalTypes = mergedClasses.getSourcesFor(previous);
+ Iterable<DexType> currentType = IterableUtils.singleton(previous);
+ if (originalTypes == null) {
+ return currentType;
+ }
+ return Iterables.concat(currentType, originalTypes);
}
@Override
@@ -219,8 +234,8 @@
}
public VerticalClassMergerGraphLens build(
- AppView<?> appView, Map<DexType, DexType> mergedClasses) {
- if (mergedClasses.isEmpty()) {
+ AppView<?> appView, VerticallyMergedClasses mergedClasses) {
+ if (mergedClasses.getForwardMap().isEmpty()) {
return null;
}
BiMap<DexField, DexField> originalFieldSignatures = fieldMap.inverse();
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 f4c003e..ea902d5 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -166,6 +166,7 @@
itemFactory = proguardConfiguration.getDexItemFactory();
enableTreeShaking = proguardConfiguration.isShrinking();
enableMinification = proguardConfiguration.isObfuscating();
+ // TODO(b/171457102): Avoid the need for this.
// -dontoptimize disables optimizations by flipping related flags.
if (!proguardConfiguration.isOptimizing()) {
disableAllOptimizations();
@@ -234,8 +235,7 @@
public boolean enableFieldBitAccessAnalysis =
System.getProperty("com.android.tools.r8.fieldBitAccessAnalysis") != null;
public boolean enableStaticClassMerging = true;
- public boolean enableHorizontalClassMerging =
- System.getProperty("com.android.tools.r8.horizontalClassMerging") != null;
+ public boolean enableHorizontalClassMerging = true;
public boolean enableVerticalClassMerging = true;
public boolean enableArgumentRemoval = true;
public boolean enableUnusedInterfaceRemoval = true;
diff --git a/src/main/java/com/android/tools/r8/utils/IterableUtils.java b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
index 15a8198..57cf52b 100644
--- a/src/main/java/com/android/tools/r8/utils/IterableUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
@@ -4,9 +4,12 @@
package com.android.tools.r8.utils;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.function.Function;
import java.util.function.Predicate;
public class IterableUtils {
@@ -53,4 +56,25 @@
public static <T> boolean isEmpty(Iterable<T> iterable) {
return !iterable.iterator().hasNext();
}
+
+ public static <T> Iterable<T> singleton(T element) {
+ return () -> Iterators.singletonIterator(element);
+ }
+
+ public static <T> Iterable<T> prependSingleton(T t, Iterable<T> iterable) {
+ return Iterables.concat(singleton(t), iterable);
+ }
+
+ public static <T, U> Iterable<U> flatMap(
+ Iterable<T> iterable, Function<? super T, Iterable<U>> map) {
+ return Iterables.concat(Iterables.transform(iterable, val -> map.apply(val)));
+ }
+
+ public static <T> Iterable<T> emptyIf(Iterable<T> iterable, boolean condition) {
+ if (condition) {
+ return Collections.emptySet();
+ } else {
+ return iterable;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index f941485..51821c0 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -7,6 +7,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -63,4 +64,12 @@
}
return true;
}
+
+ public static <T, R> R fold(Collection<T> items, R identity, BiFunction<R, T, R> acc) {
+ R result = identity;
+ for (T item : items) {
+ result = acc.apply(result, item);
+ }
+ return result;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java b/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
new file mode 100644
index 0000000..7f48f2a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2020, 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.utils;
+
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.TypeReference;
+import java.util.Iterator;
+
+public class MethodReferenceUtils {
+
+ public static String toSourceStringWithoutHolderAndReturnType(MethodReference methodReference) {
+ return toSourceString(methodReference, false, false);
+ }
+
+ public static String toSourceString(
+ MethodReference methodReference, boolean includeHolder, boolean includeReturnType) {
+ StringBuilder builder = new StringBuilder();
+ if (includeReturnType) {
+ builder.append(methodReference.getReturnType().getTypeName()).append(" ");
+ }
+ if (includeHolder) {
+ builder.append(methodReference.getHolderClass().getTypeName()).append(".");
+ }
+ builder.append(methodReference.getMethodName()).append("(");
+ Iterator<TypeReference> formalTypesIterator = methodReference.getFormalTypes().iterator();
+ if (formalTypesIterator.hasNext()) {
+ builder.append(formalTypesIterator.next().getTypeName());
+ while (formalTypesIterator.hasNext()) {
+ builder.append(", ").append(formalTypesIterator.next().getTypeName());
+ }
+ }
+ return builder.append(")").toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java
index 3c3d37c..7410339 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java
@@ -45,6 +45,10 @@
return inverse.getOrDefault(value, Collections.emptySet());
}
+ public Set<K> getKeysOrNull(V value) {
+ return inverse.get(value);
+ }
+
public boolean isEmpty() {
return backing.isEmpty();
}
diff --git a/src/test/java/com/android/tools/r8/ProguardVersion.java b/src/test/java/com/android/tools/r8/ProguardVersion.java
index 8c9050a..20560fc 100644
--- a/src/test/java/com/android/tools/r8/ProguardVersion.java
+++ b/src/test/java/com/android/tools/r8/ProguardVersion.java
@@ -20,6 +20,10 @@
this.version = version;
}
+ public static ProguardVersion getLatest() {
+ return V7_0_0;
+ }
+
public Path getProguardScript() {
Path scriptDirectory = Paths.get(ToolHelper.THIRD_PARTY_DIR).resolve("proguard");
if (this == V7_0_0) {
@@ -33,8 +37,12 @@
return scriptDirectory.resolve("bin/proguard.sh");
}
+ public String getVersion() {
+ return version;
+ }
+
@Override
public String toString() {
- return "Proguard " + version.toString();
+ return "Proguard " + version;
}
}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index c03cd49..0b9c896 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -31,6 +31,7 @@
@RunWith(VmTestRunner.class)
public class R8RunExamplesAndroidOTest extends RunExamplesAndroidOTest<R8Command.Builder> {
+
private static final ArrayList<String> PROGUARD_OPTIONS = Lists.newArrayList(
"-keepclasseswithmembers public class * {",
" public static void main(java.lang.String[]);",
@@ -161,7 +162,6 @@
@Override
@Test
public void lambdaDesugaringNPlus() throws Throwable {
- expectThrowsWithHorizontalClassMerging();
test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
.withMinApiLevel(ToolHelper.getMinApiLevelForDexVmNoHigherThan(AndroidApiLevel.K))
.withInterfaceMethodDesugaring(OffOrAuto.Auto)
@@ -179,7 +179,7 @@
.withBuilderTransformation(ToolHelper::allowTestProguardOptions)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 2, "lambdadesugaringnplus"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaringnplus"))
.run();
}
@@ -207,7 +207,7 @@
.run();
}
- private void checkLambdaCount(CodeInspector inspector, int expectedCount, String prefix) {
+ private void checkLambdaCount(CodeInspector inspector, int maxExpectedCount, String prefix) {
int count = 0;
for (FoundClassSubject clazz : inspector.allClasses()) {
if (clazz.isSynthesizedJavaLambdaClass() &&
@@ -215,7 +215,7 @@
count++;
}
}
- assertEquals(expectedCount, count);
+ assertEquals(maxExpectedCount, count);
}
private void checkTestMultipleInterfacesCheckCastCount(
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 27c1570..f18a09f 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -1713,32 +1713,4 @@
.compile()
.writeToZip();
}
-
- public static <E extends Throwable> void assertThrowsWithHorizontalClassMerging(
- ThrowingAction<E> action) throws E {
- try {
- action.execute();
- if (isHorizontalClassMergingEnabled()) {
- fail();
- }
- } catch (Throwable throwable) {
- if (!isHorizontalClassMergingEnabled()) {
- throw throwable;
- }
- }
- }
-
- public void expectThrowsWithHorizontalClassMerging() {
- expectThrowsWithHorizontalClassMergingIf(true);
- }
-
- public void expectThrowsWithHorizontalClassMergingIf(boolean condition) {
- if (isHorizontalClassMergingEnabled() && condition) {
- thrown.expect(Throwable.class);
- }
- }
-
- private static boolean isHorizontalClassMergingEnabled() {
- return System.getProperty("com.android.tools.r8.horizontalClassMerging") != null;
- }
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 9676011..3ad6107 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -189,6 +189,8 @@
Paths.get(LIBS_DIR, "library_desugar_conversions.zip");
public static final Path DESUGAR_LIB_JSON_FOR_TESTING =
Paths.get("src/library_desugar/desugar_jdk_libs.json");
+ public static final Path DESUGAR_LIB_JSON_FOR_TESTING_ALTERNATIVE_3 =
+ Paths.get("src/library_desugar/desugar_jdk_libs_alternative_3.json");
public static boolean isLocalDevelopment() {
return System.getProperty("local_development", "0").equals("1");
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
index bf103e4..11d410f 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/NonConstructorRelaxationTest.java
@@ -47,7 +47,6 @@
@Test
public void testStaticMethodRelaxation() throws Exception {
- expectThrowsWithHorizontalClassMerging();
String expectedOutput =
StringUtils.lines(
"A::baz()",
@@ -80,6 +79,7 @@
testForR8(parameters.getBackend())
.addProgramFiles(ToolHelper.getClassFilesForTestPackage(mainClass.getPackage()))
.enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.enableMemberValuePropagationAnnotations()
.addKeepMainRule(mainClass)
.addOptionsModification(o -> o.enableArgumentRemoval = enableArgumentRemoval)
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/privatestatic/BB.java b/src/test/java/com/android/tools/r8/accessrelaxation/privatestatic/BB.java
index 01ec674..a6c40ff 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/privatestatic/BB.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/privatestatic/BB.java
@@ -6,7 +6,9 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NeverPropagateValue;
+import com.android.tools.r8.NoHorizontalClassMerging;
+@NoHorizontalClassMerging
public class BB extends A {
@NeverInline
@NeverPropagateValue
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java
index 2665473..dbdb906 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.NeverClassInline;
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.TestParametersCollection;
@@ -34,7 +35,6 @@
@Test
public void test() throws Exception {
- expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addProgramClasses(TestClass.class, A.class, B3.class, B4.class)
.addProgramClassFileData(
@@ -53,6 +53,7 @@
.addKeepMainRule(TestClass.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
@@ -134,6 +135,7 @@
}
@NeverClassInline
+ @NoHorizontalClassMerging
static class B2 extends A {
// By hoisting B1.superBridge() to A this method bridge redundant.
@@ -160,11 +162,13 @@
// instruction targeting B3.virtualBridge() that fails with a NoSuchMethodError in the Enqueuer,
// but this should never be the case in practice.
@NeverClassInline
+ @NoHorizontalClassMerging
static class B3 extends A {}
// The fact that this class declares superBridge() and virtualBridge() should not prevent
// us from hoisting other bridges to A.
@NeverClassInline
+ @NoHorizontalClassMerging
static class B4 extends A {
@NeverInline
@@ -181,6 +185,7 @@
// This class declares the same bridges, but with different (bridge) behavior. They are candidates
// for hoisting, but will not be hoisted because it is better to hoist the bridges declared on B1.
@NeverClassInline
+ @NoHorizontalClassMerging
static class B5 extends A {
@NeverInline
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptResourceFileContentsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptResourceFileContentsTest.java
new file mode 100644
index 0000000..2c4ac9d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptResourceFileContentsTest.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2020, 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 com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.classmerging.horizontal.EmptyClassTest.B;
+import com.android.tools.r8.classmerging.horizontal.EmptyClassTest.Main;
+import com.android.tools.r8.classmerging.horizontal.ServiceLoaderTest.A;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.DataResourceConsumerForTesting;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class AdaptResourceFileContentsTest extends HorizontalClassMergingTestBase {
+ public AdaptResourceFileContentsTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ DataResourceConsumerForTesting dataResourceConsumer = new DataResourceConsumerForTesting();
+ CodeInspector codeInspector =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .addOptionsModification(options -> options.dataResourceConsumer = dataResourceConsumer)
+ .enableNeverClassInliningAnnotations()
+ .addDataEntryResources(
+ DataEntryResource.fromString(
+ "foo.txt", Origin.unknown(), A.class.getTypeName(), B.class.getTypeName()))
+ .addKeepRules("-adaptresourcefilecontents foo.txt")
+ .setMinApi(parameters.getApiLevel())
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging,
+ inspector -> inspector.assertMergedInto(B.class, A.class))
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("a", "b")
+ .inspector();
+
+ ClassSubject aClassSubject = codeInspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ ClassSubject bClassSubject = codeInspector.clazz(B.class);
+ assertThat(bClassSubject, notIf(isPresent(), enableHorizontalClassMerging));
+
+ // Check that the class name has been rewritten.
+ String newClassBName =
+ (enableHorizontalClassMerging ? aClassSubject : bClassSubject).getFinalName();
+ assertEquals(
+ dataResourceConsumer.get("foo.txt"),
+ ImmutableList.of(aClassSubject.getFinalName(), newClassBName));
+ }
+
+ @NeverClassInline
+ public static class A {
+ public A() {
+ System.out.println("a");
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ public B() {
+ System.out.println("b");
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ new A();
+ new B();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptVerticallyMergedResourceFileContentsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptVerticallyMergedResourceFileContentsTest.java
new file mode 100644
index 0000000..ea50082
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptVerticallyMergedResourceFileContentsTest.java
@@ -0,0 +1,107 @@
+// Copyright (c) 2020, 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 com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.DataResourceConsumerForTesting;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class AdaptVerticallyMergedResourceFileContentsTest extends HorizontalClassMergingTestBase {
+ public AdaptVerticallyMergedResourceFileContentsTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ DataResourceConsumerForTesting dataResourceConsumer = new DataResourceConsumerForTesting();
+ CodeInspector codeInspector =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .addOptionsModification(options -> options.dataResourceConsumer = dataResourceConsumer)
+ .enableNeverClassInliningAnnotations()
+ .addDataEntryResources(
+ DataEntryResource.fromString(
+ "foo.txt",
+ Origin.unknown(),
+ Parent.class.getTypeName(),
+ A.class.getTypeName(),
+ B.class.getTypeName()))
+ .addKeepRules("-adaptresourcefilecontents foo.txt")
+ .setMinApi(parameters.getApiLevel())
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging,
+ inspector -> inspector.assertMergedInto(B.class, A.class))
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("a", "foo parent", "b")
+ .inspector();
+
+ assertThat(codeInspector.clazz(Parent.class), not(isPresent()));
+
+ ClassSubject aClassSubject = codeInspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ ClassSubject bClassSubject = codeInspector.clazz(B.class);
+ assertThat(bClassSubject, notIf(isPresent(), enableHorizontalClassMerging));
+
+ // Check that the class name has been rewritten.
+ String newClassName =
+ (enableHorizontalClassMerging ? aClassSubject : bClassSubject).getFinalName();
+ assertEquals(
+ dataResourceConsumer.get("foo.txt"),
+ ImmutableList.of(aClassSubject.getFinalName(), aClassSubject.getFinalName(), newClassName));
+ }
+
+ @NeverClassInline
+ public static class Parent {
+ @NeverInline
+ public void foo() {
+ System.out.println("foo parent");
+ }
+ }
+
+ @NeverClassInline
+ public static class A extends Parent {
+ public A() {
+ System.out.println("a");
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ public B() {
+ System.out.println("b");
+ }
+ }
+
+ public static class Main {
+ @NeverInline
+ public static void parent(Parent p) {
+ p.foo();
+ }
+
+ public static void main(String[] args) {
+ parent(new A());
+ new B();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithOverlappingVisibilitiesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithOverlappingVisibilitiesTest.java
index 7dae8b6..565c1bd 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithOverlappingVisibilitiesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithOverlappingVisibilitiesTest.java
@@ -44,16 +44,20 @@
ClassSubject bClassSubject = codeInspector.clazz(B.class);
assertThat(bClassSubject, isPresent());
- methodSubject = bClassSubject.method("void", "foo");
- assertThat(methodSubject, isPackagePrivate());
+ if (enableHorizontalClassMerging) {
+ methodSubject = bClassSubject.method("void", "foo$bridge");
+ assertThat(methodSubject, isPackagePrivate());
+ }
assertThat(
codeInspector.clazz(C.class), notIf(isPresent(), enableHorizontalClassMerging));
ClassSubject dClassSubject = codeInspector.clazz(D.class);
assertThat(dClassSubject, isPresent());
- methodSubject = dClassSubject.method("void", "foo");
- assertThat(methodSubject, isPublic());
+ if (enableHorizontalClassMerging) {
+ methodSubject = dClassSubject.method("void", "foo$bridge");
+ assertThat(methodSubject, isPublic());
+ }
ClassSubject eClassSubject = codeInspector.clazz(E.class);
assertThat(eClassSubject, notIf(isPresent(), enableHorizontalClassMerging));
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java
index 9783627..185e090 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java
@@ -60,7 +60,7 @@
assertThat(
otherInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
- MethodSubject printSubject = aClassSubject.method("void", "print");
+ MethodSubject printSubject = aClassSubject.method("void", "print$bridge");
assertThat(printSubject, isPresent());
assertThat(printSubject, readsInstanceField(classIdFieldSubject.getDexField()));
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
index 109b0bc..a30149f 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
@@ -67,7 +67,7 @@
assertThat(
otherInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
- MethodSubject printSubject = aClassSubject.method("void", "print");
+ MethodSubject printSubject = aClassSubject.method("void", "print$bridge");
assertThat(printSubject, isPresent());
assertThat(printSubject, readsInstanceField(classIdFieldSubject.getDexField()));
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java
index fea20ed..f6def29 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java
@@ -60,7 +60,7 @@
assertThat(
otherInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
- MethodSubject printSubject = aClassSubject.method("void", "print");
+ MethodSubject printSubject = aClassSubject.method("void", "print$bridge");
assertThat(printSubject, isPresent());
assertThat(printSubject, readsInstanceField(classIdFieldSubject.getDexField()));
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InstantiatedAndUninstantiatedClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InstantiatedAndUninstantiatedClassMergingTest.java
new file mode 100644
index 0000000..a27fdbd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InstantiatedAndUninstantiatedClassMergingTest.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2020, 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 com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import org.junit.Test;
+
+public class InstantiatedAndUninstantiatedClassMergingTest extends HorizontalClassMergingTestBase {
+
+ public InstantiatedAndUninstantiatedClassMergingTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ test(testForR8(parameters.getBackend()));
+ }
+
+ @Test
+ public void testR8Compat() throws Exception {
+ test(testForR8Compat(parameters.getBackend()));
+ }
+
+ private <T extends R8TestBuilder<T>> void test(T testBuilder) throws Exception {
+ testBuilder
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .addHorizontallyMergedClassesInspector(
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ assertThat(inspector.clazz(Instantiated.class), isPresent());
+ assertThat(inspector.clazz(Uninstantiated.class), isPresent());
+ })
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Instantiated", "Uninstantiated");
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ new Instantiated();
+ Uninstantiated.method();
+ }
+ }
+
+ @NeverClassInline
+ public static final class Instantiated {
+
+ Instantiated() {
+ System.out.println("Instantiated");
+ }
+ }
+
+ public static final class Uninstantiated {
+
+ @NeverInline
+ static void method() {
+ System.out.println("Uninstantiated");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java
index 605ff5e..2256e5d 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java
@@ -57,7 +57,7 @@
assertThat(
otherInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
- MethodSubject printSubject = aClassSubject.method("void", "print");
+ MethodSubject printSubject = aClassSubject.method("void", "print$bridge");
assertThat(printSubject, isPresent());
assertThat(printSubject, readsInstanceField(classIdFieldSubject.getDexField()));
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedSuperMethodIsDefaultMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedSuperMethodIsDefaultMethodTest.java
index 2fa8996..483e702 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedSuperMethodIsDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedSuperMethodIsDefaultMethodTest.java
@@ -54,6 +54,7 @@
}
}
+ @NoVerticalClassMerging
public abstract static class A implements I {}
@NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedVirtualMethodStackTraceTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedVirtualMethodStackTraceTest.java
new file mode 100644
index 0000000..00d6e59
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedVirtualMethodStackTraceTest.java
@@ -0,0 +1,123 @@
+// Copyright (c) 2020, 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.naming.retrace.StackTrace.isSame;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MergedVirtualMethodStackTraceTest extends HorizontalClassMergingTestBase {
+ public MergedVirtualMethodStackTraceTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ public StackTrace expectedStackTrace;
+
+ @Before
+ public void setup() throws Exception {
+ // Get the expected stack trace by running on the JVM.
+ expectedStackTrace =
+ testForJvm()
+ .addTestClasspath()
+ .run(CfRuntime.getSystemRuntime(), Program.Main.class)
+ .assertFailure()
+ .map(StackTrace::extractFromJvm);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(Program.class)
+ .addKeepMainRule(Program.Main.class)
+ .addKeepAttributeLineNumberTable()
+ .addKeepAttributeSourceFile()
+ .addDontWarn(C.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging,
+ inspector -> inspector.assertMergedInto(Program.B.class, Program.A.class))
+ .run(parameters.getRuntime(), Program.Main.class)
+ .inspectStackTrace(
+ (stackTrace, codeInspector) -> {
+ assertThat(codeInspector.clazz(Program.A.class), isPresent());
+ assertThat(
+ codeInspector.clazz(Program.B.class),
+ notIf(isPresent(), enableHorizontalClassMerging));
+ if (enableHorizontalClassMerging) {
+ StackTrace expectedStackTraceWithMergedMethod =
+ StackTrace.builder()
+ .add(expectedStackTrace)
+
+ .add(
+ 1,
+ StackTraceLine.builder()
+ .setClassName(Program.A.class.getTypeName())
+ .setMethodName("foo$bridge")
+ .setFileName("Program.java")
+ .setFileName(getClass().getSimpleName() + ".java")
+ .setLineNumber(stackTrace.get(1).lineNumber)
+ .build())
+ .build();
+ assertThat(stackTrace, isSame(expectedStackTraceWithMergedMethod));
+ }
+ });
+ }
+
+ public static class C {
+ public static void foo() {
+ System.out.println("foo c");
+ }
+ }
+
+ public static class Program {
+ @NeverClassInline
+ public static class A {
+ @NeverInline
+ public void foo() {
+ System.out.println("foo a");
+ try {
+ // Undefined reference, prevents inlining.
+ C.foo();
+ } catch (NoClassDefFoundError e) {
+ }
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ @NeverInline
+ public void foo() {
+ try {
+ // Undefined reference, prevents inlining.
+ C.foo();
+ } catch (NoClassDefFoundError e) {
+ }
+ throw new RuntimeException();
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ new A().foo();
+ new B().foo();
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java b/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java
index 412311e..de0e481 100644
--- a/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java
+++ b/src/test/java/com/android/tools/r8/compatproguard/CompatKeepClassMemberNamesTestRunner.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.BaseCompilerCommand;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.ProguardVersion;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestCompileResult;
import com.android.tools.r8.TestParameters;
@@ -35,6 +36,8 @@
@RunWith(Parameterized.class)
public class CompatKeepClassMemberNamesTestRunner extends TestBase {
+ private static final ProguardVersion PG = ProguardVersion.getLatest();
+
private static Class<?> MAIN_CLASS = CompatKeepClassMemberNamesTest.class;
private static Class<?> BAR_CLASS = CompatKeepClassMemberNamesTest.Bar.class;
private static Collection<Class<?>> CLASSES =
@@ -114,7 +117,7 @@
@Test
public void testWithoutRulesPG() throws Exception {
- testWithoutRules(testForProguard());
+ testWithoutRules(testForProguard(PG));
}
@Test
@@ -157,7 +160,7 @@
@Test
public void testWithMembersRulePG() throws Exception {
- assertMembersRuleCompatResult(buildWithMembersRule(testForProguard()).compile());
+ assertMembersRuleCompatResult(buildWithMembersRule(testForProguard(PG)).compile());
}
@Test
@@ -209,7 +212,7 @@
@Test
public void testWithNonStaticMembersRulePG() throws Exception {
- assertBarIsAbsent(buildWithNonStaticMembersRule(testForProguard()).compile());
+ assertBarIsAbsent(buildWithNonStaticMembersRule(testForProguard(PG)).compile());
}
@Test
@@ -268,7 +271,7 @@
@Test
public void testWithMembersRuleEnableMinificationPG() throws Exception {
assertMembersRuleEnableMinificationCompatResult(
- buildWithMembersRuleEnableMinification(testForProguard()).compile());
+ buildWithMembersRuleEnableMinification(testForProguard(PG)).compile());
}
@Test
@@ -313,7 +316,7 @@
@Test
public void testWithMembersStarRulePG() throws Exception {
- testWithMembersStarRule(testForProguard());
+ testWithMembersStarRule(testForProguard(PG));
}
@Test
@@ -362,7 +365,7 @@
@Test
public void testWithMemberNamesRulePG() throws Exception {
- assertMemberNamesRuleCompatResult(buildWithMemberNamesRule(testForProguard()).compile());
+ assertMemberNamesRuleCompatResult(buildWithMemberNamesRule(testForProguard(PG)).compile());
}
@Test
@@ -375,7 +378,8 @@
@Test
public void testWithMemberNamesRuleFullR8() throws Exception {
- assertBarIsAbsent(buildWithMemberNamesRule(testForR8Compat(parameters.getBackend())).compile());
+ assertMemberNamesRuleCompatResult(
+ buildWithMemberNamesRule(testForR8(parameters.getBackend())).compile());
}
// Tests for "-keepclassmembernames" and *no* minification.
@@ -419,7 +423,7 @@
@Test
public void testWithMemberNamesRuleEnableMinificationPG() throws Exception {
assertMemberNamesRuleEnableMinificationCompatResult(
- buildWithMemberNamesRuleEnableMinification(testForProguard()).compile());
+ buildWithMemberNamesRuleEnableMinification(testForProguard(PG)).compile());
}
@Test
@@ -432,7 +436,7 @@
@Test
public void testWithMemberNamesRuleEnableMinificationFullR8() throws Exception {
- assertBarIsAbsent(
+ assertMemberNamesRuleEnableMinificationCompatResult(
buildWithMemberNamesRuleEnableMinification(testForR8(parameters.getBackend())).compile());
}
}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index 44bcc80..13d59dc 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -2108,6 +2108,14 @@
}
return false;
}
+ if (isInLambdaClass(mirror, location)) {
+ // Lambda classes must be skipped since they are only wrappers around lambda code.
+ if (DEBUG_TESTS) {
+ System.out.println("Skipping lambda class wrapper method");
+ }
+ wrapper.enqueueCommandFirst(stepCommand);
+ return true;
+ }
if (isSyntheticMethod(mirror, location)) {
if (DEBUG_TESTS) {
System.out.println("Skipping synthetic method");
@@ -2152,6 +2160,11 @@
return false;
}
+ private static boolean isInLambdaClass(VmMirror mirror, Location location) {
+ String classSig = mirror.getClassSignature(location.classID);
+ return classSig.contains("$$Lambda$");
+ }
+
private static boolean isLambdaMethod(VmMirror mirror, Location location) {
String methodName = mirror.getMethodName(location.classID, location.methodID);
return methodName.startsWith("lambda$");
diff --git a/src/test/java/com/android/tools/r8/desugar/LambdaHasNonSyntheticMethodTest.java b/src/test/java/com/android/tools/r8/desugar/LambdaHasNonSyntheticMethodTest.java
new file mode 100644
index 0000000..656cea6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/LambdaHasNonSyntheticMethodTest.java
@@ -0,0 +1,83 @@
+// Copyright (c) 2020, 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.desugar;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class LambdaHasNonSyntheticMethodTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("Hello, world");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ public LambdaHasNonSyntheticMethodTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addInnerClasses(getClass())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ interface MyCallable<T> {
+ T call() throws Exception;
+ }
+
+ static class TestClass {
+
+ private static void assertNotNull(Object o) {
+ if (o == null) throw new AssertionError();
+ }
+
+ private static void assertSame(Object o1, Object o2) {
+ if (o1 != o2) throw new AssertionError();
+ }
+
+ private static void assertFalse(boolean value) {
+ if (value) throw new AssertionError();
+ }
+
+ private static <T> void assertLambdaMethodIsNotSynthetic(T instance, Class<?> iface)
+ throws Exception {
+ Method ifaceMethod = null;
+ for (Method method : iface.getDeclaredMethods()) {
+ if (Modifier.isAbstract(method.getModifiers())) {
+ ifaceMethod = method;
+ break;
+ }
+ }
+ assertNotNull(ifaceMethod);
+ Method lambdaMethod =
+ instance.getClass().getMethod(ifaceMethod.getName(), ifaceMethod.getParameterTypes());
+ assertSame(ifaceMethod.getReturnType(), lambdaMethod.getReturnType());
+ assertSame(instance.getClass(), lambdaMethod.getDeclaringClass());
+ assertFalse(lambdaMethod.isSynthetic());
+ assertFalse(lambdaMethod.isBridge());
+ }
+
+ public static void main(String[] args) throws Exception {
+ StringBuilder builder = new StringBuilder("Hello, world");
+ MyCallable<String> instance = builder::toString;
+ assertLambdaMethodIsNotSynthetic(instance, MyCallable.class);
+ System.out.println(instance.call());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
index c2a8275..f71c916 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
@@ -60,7 +60,7 @@
: "Caught j$.io.UncheckedIOException");
}
- DesugaredLibraryConfiguration configurationWithBufferedReader(
+ DesugaredLibraryConfiguration configurationAlternative3(
InternalOptions options, boolean libraryCompilation, TestParameters parameters) {
// Parse the current configuration and amend the configuration for BufferedReader.lines. The
// configuration is the same for both program and library.
@@ -69,28 +69,15 @@
options.reporter,
libraryCompilation,
parameters.getApiLevel().getLevel())
- .parse(
- StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING),
- builder -> {
- if (parameters.getApiLevel().isLessThan(AndroidApiLevel.N)) {
- builder.putRewritePrefix(
- "java.io.DesugarBufferedReader", "j$.io.DesugarBufferedReader");
- builder.putRewritePrefix(
- "java.io.UncheckedIOException", "j$.io.UncheckedIOException");
- builder.putRetargetCoreLibMember(
- "java.io.BufferedReader#lines", "java.io.DesugarBufferedReader");
- }
- });
+ .parse(StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING_ALTERNATIVE_3));
}
private void configurationForProgramCompilation(InternalOptions options) {
- options.desugaredLibraryConfiguration =
- configurationWithBufferedReader(options, false, parameters);
+ options.desugaredLibraryConfiguration = configurationAlternative3(options, false, parameters);
}
private void configurationForLibraryCompilation(InternalOptions options) {
- options.desugaredLibraryConfiguration =
- configurationWithBufferedReader(options, true, parameters);
+ options.desugaredLibraryConfiguration = configurationAlternative3(options, true, parameters);
}
@Test
@@ -156,7 +143,7 @@
.addOptionsModification(
options ->
options.desugaredLibraryConfiguration =
- configurationWithBufferedReader(options, false, parameters))
+ configurationAlternative3(options, false, parameters))
.addInnerClasses(BufferedReaderTest.class)
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
@@ -184,7 +171,7 @@
.addOptionsModification(
options ->
options.desugaredLibraryConfiguration =
- configurationWithBufferedReader(options, false, parameters))
+ configurationAlternative3(options, false, parameters))
.addInnerClasses(BufferedReaderTest.class)
.addKeepMainRule(TestClass.class)
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
index b02b2e9..c7465ca 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
@@ -21,6 +21,8 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.InternalOptions;
@@ -67,6 +69,11 @@
return buildDesugaredLibrary(apiLevel, "", false);
}
+ protected Path buildDesugaredLibrary(
+ AndroidApiLevel apiLevel, Consumer<InternalOptions> optionsModifier) {
+ return buildDesugaredLibrary(apiLevel, "", false, ImmutableList.of(), optionsModifier);
+ }
+
protected Path buildDesugaredLibrary(AndroidApiLevel apiLevel, String keepRules) {
return buildDesugaredLibrary(apiLevel, keepRules, true);
}
@@ -186,6 +193,21 @@
return desugaredLib;
}
+ protected DesugaredLibraryConfiguration configurationWithSupportAllCallbacksFromLibrary(
+ InternalOptions options,
+ boolean libraryCompilation,
+ TestParameters parameters,
+ boolean supportAllCallbacksFromLibrary) {
+ return new DesugaredLibraryConfigurationParser(
+ options.dexItemFactory(),
+ options.reporter,
+ libraryCompilation,
+ parameters.getApiLevel().getLevel())
+ .parse(
+ StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING),
+ builder -> builder.setSupportAllCallbacksFromLibrary(supportAllCallbacksFromLibrary));
+ }
+
public interface KeepRuleConsumer extends StringConsumer {
String get();
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
index 14f3ded..e90d8d2 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import java.nio.file.Path;
import java.util.Collection;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
@@ -34,6 +35,7 @@
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
+ private final boolean supportAllCallbacksFromLibrary;
private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.N;
private static final String EXPECTED_RESULT =
@@ -42,16 +44,24 @@
"forEach called",
"action called from java consumer",
"forEach called");
+ private static final String FAILING_EXPECTED_RESULT =
+ StringUtils.lines(
+ "action called from j$ consumer", "forEach called", "action called from java consumer");
private static Path CUSTOM_LIB;
- @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
+ @Parameters(name = "{0}, shrink: {1}, supportCallbacks: {2}")
public static List<Object[]> data() {
return buildParameters(
- getConversionParametersUpToExcluding(MIN_SUPPORTED), BooleanUtils.values());
+ getConversionParametersUpToExcluding(MIN_SUPPORTED),
+ BooleanUtils.values(),
+ BooleanUtils.values());
}
public ConversionIntroduceInterfaceMethodTest(
- TestParameters parameters, boolean shrinkDesugaredLibrary) {
+ TestParameters parameters,
+ boolean shrinkDesugaredLibrary,
+ boolean supportAllCallbacksFromLibrary) {
+ this.supportAllCallbacksFromLibrary = supportAllCallbacksFromLibrary;
this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
this.parameters = parameters;
}
@@ -75,6 +85,11 @@
.addLibraryClasses(CustomLibClass.class)
.addOptionsModification(opt -> opt.testing.trackDesugaredAPIConversions = true)
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .addOptionsModification(
+ opt ->
+ opt.desugaredLibraryConfiguration =
+ configurationWithSupportAllCallbacksFromLibrary(
+ opt, false, parameters, supportAllCallbacksFromLibrary))
.compile()
.inspect(this::assertDoubleForEach)
.inspect(this::assertWrapperMethodsPresent)
@@ -85,11 +100,13 @@
shrinkDesugaredLibrary)
.addRunClasspathFiles(CUSTOM_LIB)
.run(parameters.getRuntime(), Executor.class)
- .assertSuccessWithOutput(EXPECTED_RESULT);
+ .apply(
+ r ->
+ r.assertSuccessWithOutput(
+ supportAllCallbacksFromLibrary ? EXPECTED_RESULT : FAILING_EXPECTED_RESULT));
}
private void assertDoubleForEach(CodeInspector inspector) {
- System.out.println(inspector.allClasses().size());
FoundClassSubject myCollection =
inspector.allClasses().stream()
.filter(
@@ -103,7 +120,7 @@
.collect(toSingle());
assertEquals(
"Missing duplicated forEach",
- 2,
+ supportAllCallbacksFromLibrary ? 2 : 1,
IterableUtils.size(
myCollection
.getDexProgramClass()
@@ -133,6 +150,11 @@
.addKeepMainRule(Executor.class)
.addLibraryClasses(CustomLibClass.class)
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .addOptionsModification(
+ opt ->
+ opt.desugaredLibraryConfiguration =
+ configurationWithSupportAllCallbacksFromLibrary(
+ opt, false, parameters, supportAllCallbacksFromLibrary))
.compile()
.inspect(this::assertDoubleForEach)
.inspect(this::assertWrapperMethodsPresent)
@@ -143,7 +165,10 @@
shrinkDesugaredLibrary)
.addRunClasspathFiles(CUSTOM_LIB)
.run(parameters.getRuntime(), Executor.class)
- .assertSuccessWithOutput(EXPECTED_RESULT);
+ .apply(
+ r ->
+ r.assertSuccessWithOutput(
+ supportAllCallbacksFromLibrary ? EXPECTED_RESULT : FAILING_EXPECTED_RESULT));
}
static class CustomLibClass {
@@ -197,7 +222,7 @@
@Override
public Iterator<E> iterator() {
- return null;
+ return (Iterator<E>) Collections.singletonList(null).iterator();
}
@NotNull
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIDesugaredLibTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIDesugaredLibTest.java
index 1f433d4..812cd34 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIDesugaredLibTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/DuplicateAPIDesugaredLibTest.java
@@ -6,55 +6,84 @@
import static junit.framework.TestCase.assertEquals;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime.DexRuntime;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.nio.file.Path;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+@RunWith(Parameterized.class)
public class DuplicateAPIDesugaredLibTest extends DesugaredLibraryTestBase {
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+ private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.N;
+
+ @Parameterized.Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getConversionParametersUpToExcluding(MIN_SUPPORTED), BooleanUtils.values());
+ }
+
+ public DuplicateAPIDesugaredLibTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
@Test
public void testLib() throws Exception {
- Box<Path> desugaredLibBox = new Box<>();
- Path customLib =
- testForD8()
- .addProgramClasses(CustomLibClass.class)
- .setMinApi(AndroidApiLevel.B)
- .compile()
- .writeToZip();
- String stdOut =
- testForD8()
- .setMinApi(AndroidApiLevel.B)
- .addProgramClasses(Executor.class)
- .addLibraryClasses(CustomLibClass.class)
- .enableCoreLibraryDesugaring(AndroidApiLevel.B)
- .compile()
- .addDesugaredCoreLibraryRunClassPath(
- (AndroidApiLevel api) -> {
- desugaredLibBox.set(this.buildDesugaredLibrary(api));
- return desugaredLibBox.get();
- },
- AndroidApiLevel.B)
- .addRunClasspathFiles(customLib)
- .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
- .assertSuccess()
- .getStdOut();
- assertDupMethod(new CodeInspector(desugaredLibBox.get()));
- assertLines2By2Correct(stdOut);
+ for (Boolean supportAllCallbacksFromLibrary : BooleanUtils.values()) {
+ Box<Path> desugaredLibBox = new Box<>();
+ Path customLib =
+ testForD8()
+ .addProgramClasses(CustomLibClass.class)
+ .setMinApi(AndroidApiLevel.B)
+ .compile()
+ .writeToZip();
+ String stdOut =
+ testForD8()
+ .setMinApi(AndroidApiLevel.B)
+ .addProgramClasses(Executor.class)
+ .addLibraryClasses(CustomLibClass.class)
+ .enableCoreLibraryDesugaring(AndroidApiLevel.B)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ (AndroidApiLevel api) -> {
+ desugaredLibBox.set(
+ this.buildDesugaredLibrary(
+ api,
+ opt ->
+ opt.desugaredLibraryConfiguration =
+ configurationWithSupportAllCallbacksFromLibrary(
+ opt, true, parameters, supportAllCallbacksFromLibrary)));
+ return desugaredLibBox.get();
+ },
+ AndroidApiLevel.B)
+ .addRunClasspathFiles(customLib)
+ .run(new DexRuntime(DexVm.ART_9_0_0_HOST), Executor.class)
+ .assertSuccess()
+ .getStdOut();
+ assertDupMethod(new CodeInspector(desugaredLibBox.get()), supportAllCallbacksFromLibrary);
+ assertLines2By2Correct(stdOut);
+ }
}
- private void assertDupMethod(CodeInspector inspector) {
+ private void assertDupMethod(CodeInspector inspector, boolean supportAllCallbacksFromLibrary) {
ClassSubject clazz = inspector.clazz("j$.util.concurrent.ConcurrentHashMap");
assertEquals(
- 2,
+ supportAllCallbacksFromLibrary ? 2 : 1,
clazz.virtualMethods().stream().filter(m -> m.getOriginalName().equals("forEach")).count());
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/gson.cfg b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/gson.cfg
index da7bcba..e7560e1f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/gson.cfg
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/gson.cfg
@@ -14,11 +14,11 @@
-dontwarn sun.misc.Unsafe
# Application classes that will be serialized/deserialized over Gson
--keep class com.android.tools.r8.desugar.desugaredlibrary.gson.AllMapsTestClasses$Data { <fields>; }
--keep class com.android.tools.r8.desugar.desugaredlibrary.gson.AllMapsTestClasses$NullableConcurrentHashMap
--keep class com.android.tools.r8.desugar.desugaredlibrary.gson.AllMapsTestClasses$NullableHashMap
--keep class com.android.tools.r8.desugar.desugaredlibrary.gson.AllMapsTestClasses$NullableMap
--keep class com.android.tools.r8.desugar.desugaredlibrary.gson.AllMapsTestClasses$NullableConcurrentMap
+-keep class com.android.tools.r8.desugar.desugaredlibrary.gson.AllMapsTestClass$Data { <fields>; }
+-keep class com.android.tools.r8.desugar.desugaredlibrary.gson.AllMapsTestClass$NullableConcurrentHashMap
+-keep class com.android.tools.r8.desugar.desugaredlibrary.gson.AllMapsTestClass$NullableHashMap
+-keep class com.android.tools.r8.desugar.desugaredlibrary.gson.AllMapsTestClass$NullableMap
+-keep class com.android.tools.r8.desugar.desugaredlibrary.gson.AllMapsTestClass$NullableConcurrentMap
-keep class com.android.tools.r8.desugar.desugaredlibrary.gson.OptionalTestClass$Data { <fields>; }
# Prevent R8 from stripping interface information from TypeAdapter, TypeAdapterFactory,
@@ -28,6 +28,9 @@
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
+# Prevent R8 from removing the generic signature of TypeToken
+-keep,allowobfuscation class * extends com.google.gson.reflect.TypeToken
+
# Prevent R8 from leaving Data object members always null
-keepclassmembers,allowobfuscation class * {
@com.google.gson.annotations.SerializedName <fields>;
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
index a04b0c1..5a788bf 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
@@ -57,11 +57,8 @@
@BeforeClass
public static void beforeAll() throws Exception {
- assertThrowsWithHorizontalClassMerging(
- () -> {
- r8Lib11NoDesugar = compileR8(false);
- r8Lib11Desugar = compileR8(true);
- });
+ r8Lib11NoDesugar = compileR8(false);
+ r8Lib11Desugar = compileR8(true);
}
private static Path compileR8(boolean desugar) throws Exception {
diff --git a/src/test/java/com/android/tools/r8/graph/initializedclasses/InitializedClassesInInstanceMethodsTest.java b/src/test/java/com/android/tools/r8/graph/initializedclasses/InitializedClassesInInstanceMethodsTest.java
index da5dc5c..8256c29 100644
--- a/src/test/java/com/android/tools/r8/graph/initializedclasses/InitializedClassesInInstanceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/graph/initializedclasses/InitializedClassesInInstanceMethodsTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.NeverClassInline;
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.utils.BooleanUtils;
@@ -43,7 +44,6 @@
@Test
public void test() throws Exception {
- expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(InitializedClassesInInstanceMethodsTest.class)
.addKeepMainRule(TestClass.class)
@@ -54,6 +54,7 @@
})
.allowAccessModification()
.enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
@@ -137,6 +138,7 @@
}
@NeverClassInline
+ @NoHorizontalClassMerging
static class B {
@NeverInline
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLatestTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLatestTreeShakeJarVerificationTest.java
index b7bff95..d0242b4 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLatestTreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLatestTreeShakeJarVerificationTest.java
@@ -29,7 +29,6 @@
List<String> additionalProguardConfiguration =
ImmutableList.of(
ToolHelper.PROGUARD_SETTINGS_FOR_INTERNAL_APPS + "GmsCore_proguard.config");
-
Map<String, IntSet> methodProcessingIds = new ConcurrentHashMap<>();
AndroidApp app1 =
buildAndTreeShakeFromDeployJar(
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
index dc2b296..1375f54 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
@@ -168,6 +168,10 @@
}
private void verifyBuildersAreAbsent(CodeInspector outputInspector) {
+ // TODO(b/171441793): Should be optimized out but fails do to soft pinning of super class.
+ if (true) {
+ return;
+ }
assertThat(
outputInspector.clazz(
"com.android.tools.r8.proto2.Shrinking$HasFlaggedOffExtension$Builder"),
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/PutObjectWithFinalizeTest.java b/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/PutObjectWithFinalizeTest.java
index 2eb4a58..17e3f64 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/PutObjectWithFinalizeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/PutObjectWithFinalizeTest.java
@@ -10,6 +10,7 @@
import static org.junit.Assert.assertTrue;
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;
@@ -41,13 +42,13 @@
@Test
public void test() throws Exception {
- expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(PutObjectWithFinalizeTest.class)
.addKeepMainRule(TestClass.class)
// The class staticizer does not consider the finalize() method.
.addOptionsModification(options -> options.enableClassStaticizer = false)
.enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
@@ -148,5 +149,6 @@
static class B extends A {}
+ @NoHorizontalClassMerging
static class C {}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
index bb36308..e5746ec 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
@@ -6,12 +6,16 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersBuilder;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -31,12 +35,14 @@
private Backend backend;
- @Parameterized.Parameters(name = "Backend: {0}")
- public static Backend[] data() {
- return ToolHelper.getBackends();
+ @Parameterized.Parameters(name = "Backend: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ TestParametersBuilder.builder().withNoneRuntime().build(), ToolHelper.getBackends());
}
- public MemberValuePropagationTest(TestBase.Backend backend) {
+ public MemberValuePropagationTest(TestParameters parameters, TestBase.Backend backend) {
+ parameters.assertNoneRuntime();
this.backend = backend;
}
@@ -56,14 +62,14 @@
public void testWriteOnlyField_dontoptimize() throws Exception {
CodeInspector inspector = runR8(DONT_OPTIMIZE);
ClassSubject clazz = inspector.clazz(QUALIFIED_CLASS_NAME);
- clazz.forAllMethods(
- methodSubject -> {
- // Dead code removal is not part of -dontoptimize. That is, even with -dontoptimize,
- // field put instructions are gone with better dead code removal.
- assertTrue(
- methodSubject.streamInstructions().noneMatch(
- i -> i.isInstancePut() || i.isStaticPut()));
- });
+ // With the support of 'allowshrinking' dontoptimize will now effectively pin all
+ // items that are not tree shaken out. The field operations will thus remain.
+ assertTrue(clazz.clinit().streamInstructions().anyMatch(InstructionSubject::isStaticPut));
+ assertTrue(
+ clazz
+ .uniqueInstanceInitializer()
+ .streamInstructions()
+ .anyMatch(InstructionSubject::isInstancePut));
}
private CodeInspector runR8(Path proguardConfig) throws Exception {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index 143f206..807b061 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -131,35 +131,32 @@
@Before
public void generateR8Version() throws Exception {
- assertThrowsWithHorizontalClassMerging(
- () -> {
- outputDir = temp.newFolder().toPath();
- Path mapFile = outputDir.resolve(DEFAULT_MAP_FILENAME);
- generateR8Version(outputDir, mapFile, true);
- String output;
- if (parameters.isDexRuntime()) {
- output =
- ToolHelper.runArtNoVerificationErrors(
- Collections.singletonList(outputDir.resolve(DEFAULT_DEX_FILENAME).toString()),
- "inlining.Inlining",
- builder -> {},
- parameters.getRuntime().asDex().getVm());
- } else {
- assert parameters.isCfRuntime();
- output =
- ToolHelper.runJava(
- parameters.getRuntime().asCf(),
- Collections.singletonList("-noverify"),
- Collections.singletonList(outputDir),
- "inlining.Inlining")
- .stdout;
- }
+ outputDir = temp.newFolder().toPath();
+ Path mapFile = outputDir.resolve(DEFAULT_MAP_FILENAME);
+ generateR8Version(outputDir, mapFile, true);
+ String output;
+ if (parameters.isDexRuntime()) {
+ output =
+ ToolHelper.runArtNoVerificationErrors(
+ Collections.singletonList(outputDir.resolve(DEFAULT_DEX_FILENAME).toString()),
+ "inlining.Inlining",
+ builder -> {},
+ parameters.getRuntime().asDex().getVm());
+ } else {
+ assert parameters.isCfRuntime();
+ output =
+ ToolHelper.runJava(
+ parameters.getRuntime().asCf(),
+ Collections.singletonList("-noverify"),
+ Collections.singletonList(outputDir),
+ "inlining.Inlining")
+ .stdout;
+ }
- // Compare result with Java to make sure we have the same behavior.
- ProcessResult javaResult = ToolHelper.runJava(getInputFile(), "inlining.Inlining");
- assertEquals(0, javaResult.exitCode);
- assertEquals(javaResult.stdout, output);
- });
+ // Compare result with Java to make sure we have the same behavior.
+ ProcessResult javaResult = ToolHelper.runJava(getInputFile(), "inlining.Inlining");
+ assertEquals(0, javaResult.exitCode);
+ assertEquals(javaResult.stdout, output);
}
private void checkAbsentBooleanMethod(ClassSubject clazz, String name) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java
index aa4dd9a..3ac7e29 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java
@@ -10,6 +10,7 @@
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;
@@ -41,11 +42,11 @@
@Test
public void testR8() throws Exception {
- expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(InvokeVirtualWithRefinedReceiverTest.class)
.addKeepMainRule(MAIN)
.enableNoVerticalClassMergingAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.addOptionsModification(
@@ -148,6 +149,7 @@
}
@NoVerticalClassMerging
+ @NoHorizontalClassMerging
@NeverClassInline
static class C extends A {
@NeverInline
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/WithStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/WithStaticizerTest.java
index 3fb859c..04cbc5f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/WithStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/WithStaticizerTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.NeverClassInline;
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.TestParametersCollection;
@@ -39,12 +40,12 @@
@Test
public void testR8() throws Exception {
- expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(WithStaticizerTest.class)
.addKeepMainRule(MAIN)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("Input")
@@ -89,6 +90,7 @@
}
@NeverClassInline
+ @NoHorizontalClassMerging
static class Input {
@NeverInline
@Override
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/SingletonCanonicalizationWithApiLevelCheckTest.java b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/SingletonCanonicalizationWithApiLevelCheckTest.java
index 7f5b905..bf78732 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/SingletonCanonicalizationWithApiLevelCheckTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/SingletonCanonicalizationWithApiLevelCheckTest.java
@@ -62,15 +62,7 @@
.compile()
.addRunClasspathFiles(program)
.run(parameters.getRuntime(), TestClass.class)
- .apply(
- runResult -> {
- if (parameters.isCfRuntime()) {
- // Constant canonicalization is disabled for CF.
- runResult.assertSuccessWithEmptyOutput();
- } else {
- runResult.assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
- }
- });
+ .assertSuccessWithEmptyOutput();
}
private List<String> getAssumeValuesRule(int version) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
index c6f7772..8d73c3a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
@@ -64,7 +64,6 @@
@Test
public void testTrivial() throws Exception {
- expectThrowsWithHorizontalClassMerging();
Class<?> main = TrivialTestClass.class;
Class<?>[] classes = {
TrivialTestClass.class,
@@ -85,6 +84,7 @@
testForR8(parameters.getBackend())
.addProgramClasses(classes)
.enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.enableSideEffectAnnotations()
.addKeepMainRule(main)
.addKeepAttributes("LineNumberTable")
@@ -221,7 +221,6 @@
@Test
public void testInvalidatedRoot() throws Exception {
- expectThrowsWithHorizontalClassMerging();
Class<?> main = InvalidRootsTestClass.class;
Class<?>[] classes = {
InvalidRootsTestClass.class,
@@ -236,6 +235,7 @@
.addProgramClasses(classes)
.enableProguardTestOptions()
.enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.addKeepMainRule(main)
.addKeepAttributes("LineNumberTable")
.addOptionsModification(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/invalidroot/InvalidRootsTestClass.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/invalidroot/InvalidRootsTestClass.java
index 226d995e..2b29cea 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/invalidroot/InvalidRootsTestClass.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/invalidroot/InvalidRootsTestClass.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.classinliner.invalidroot;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
public class InvalidRootsTestClass {
private static int ID = 0;
@@ -76,12 +77,14 @@
prefix + ", " + (a == null ? "null" : a.foo()) + "): " + next());
}
+ @NoHorizontalClassMerging
public static class NeverReturnsNormally {
public String foo() {
throw new RuntimeException("NeverReturnsNormally::foo(): " + next());
}
}
+ @NoHorizontalClassMerging
public static class InitNeverReturnsNormally {
public InitNeverReturnsNormally() {
throw new RuntimeException("InitNeverReturnsNormally::init(): " + next());
@@ -131,6 +134,7 @@
}
}
+ @NoHorizontalClassMerging
public static class B {
public String foo() {
return "B::foo(" + next() + ")";
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/ClassWithFinal.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/ClassWithFinal.java
index 7e0239c..2fd1735 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/ClassWithFinal.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/ClassWithFinal.java
@@ -4,6 +4,9 @@
package com.android.tools.r8.ir.optimize.classinliner.trivial;
+import com.android.tools.r8.NoHorizontalClassMerging;
+
+@NoHorizontalClassMerging
public class ClassWithFinal {
public String doNothing() {
return "nothing at all";
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
index 287fe21..09b0ae3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
@@ -62,8 +62,6 @@
ExistingException.class)
.addKeepMainRule(TestClassCallingMethodWithNonExisting.class)
.addKeepRules("-dontwarn " + NonExistingException.class.getTypeName())
- .allowDiagnosticWarningMessages(
- parameters.isCfRuntime() || parameters.getApiLevel().isLessThan(AndroidApiLevel.N))
.setMinApi(parameters.getApiLevel())
.compile()
.assertAllWarningMessagesMatch(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
index 2012c6f..f5c7da8 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
@@ -54,6 +54,7 @@
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.enableSideEffectAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
index df009b4..9d4c185 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
@@ -180,7 +180,6 @@
"STATIC: String SimpleWithSideEffects.bar(String)",
"STATIC: String SimpleWithSideEffects.foo()",
"STATIC: String TrivialTestClass.next()",
- "SimpleWithSideEffects SimpleWithSideEffects.INSTANCE",
"SimpleWithSideEffects SimpleWithSideEffects.INSTANCE"),
references(clazz, "testSimpleWithSideEffects", "void"));
@@ -268,7 +267,6 @@
@Test
public void testMoveToHost() throws Exception {
- expectThrowsWithHorizontalClassMerging();
Class<?> main = MoveToHostTestClass.class;
Class<?>[] classes = {
NeverInline.class,
@@ -287,6 +285,7 @@
testForR8(parameters.getBackend())
.addProgramClasses(classes)
.enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.enableMemberValuePropagationAnnotations()
.addKeepMainRule(main)
.allowAccessModification()
@@ -315,7 +314,6 @@
"STATIC: String movetohost.HostOkSideEffects.bar(String)",
"STATIC: String movetohost.HostOkSideEffects.foo()",
"STATIC: String movetohost.MoveToHostTestClass.next()",
- "movetohost.HostOkSideEffects movetohost.HostOkSideEffects.INSTANCE",
"movetohost.HostOkSideEffects movetohost.HostOkSideEffects.INSTANCE"),
references(clazz, "testOkSideEffects", "void"));
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateConflictMethod.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateConflictMethod.java
index 4e5ce3a..cde24e8 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateConflictMethod.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateConflictMethod.java
@@ -5,7 +5,9 @@
package com.android.tools.r8.ir.optimize.staticizer.movetohost;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+@NoHorizontalClassMerging
public class CandidateConflictMethod {
@NeverInline
public String foo() {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateOk.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateOk.java
index 0834c000..eb8e2f6 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateOk.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateOk.java
@@ -5,7 +5,9 @@
package com.android.tools.r8.ir.optimize.staticizer.movetohost;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+@NoHorizontalClassMerging
public class CandidateOk {
@NeverInline
public String foo() {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/typechecks/InstanceOfMethodSpecializationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/typechecks/InstanceOfMethodSpecializationTest.java
index 4173bb2..ed970af 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/typechecks/InstanceOfMethodSpecializationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/typechecks/InstanceOfMethodSpecializationTest.java
@@ -9,6 +9,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -50,10 +51,10 @@
@Test
public void test() throws Exception {
- expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(InstanceOfMethodSpecializationTest.class)
.addKeepMainRule(TestClass.class)
+ .enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
@@ -157,6 +158,7 @@
}
}
+ @NoHorizontalClassMerging
public static class C extends A {
@Override
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/PrivateInstanceMethodCollisionTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/PrivateInstanceMethodCollisionTest.java
index e14bb7a..0de94c9 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/PrivateInstanceMethodCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/PrivateInstanceMethodCollisionTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.NeverClassInline;
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.utils.BooleanUtils;
@@ -48,7 +49,6 @@
@Test
public void b139769782() throws Exception {
- expectThrowsWithHorizontalClassMerging();
String expectedOutput = StringUtils.lines("A#foo(B)", "A#foo(B, Object)");
if (parameters.isCfRuntime() && !minification && !allowAccessModification) {
@@ -63,6 +63,7 @@
.addKeepMainRule(TestClass.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.minification(minification)
.allowAccessModification(allowAccessModification)
.setMinApi(parameters.getRuntime())
@@ -122,6 +123,7 @@
}
}
+ @NoHorizontalClassMerging
static class B {
@Override
public String toString() {
@@ -129,5 +131,6 @@
}
}
+ @NoHorizontalClassMerging
static class C {}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java
new file mode 100644
index 0000000..3a795be
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2019, 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.kotlin.lambda;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
+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 KotlinLambdaMergingDebugTest extends AbstractR8KotlinTestBase {
+
+ private final TestParameters parameters;
+ private static final String FOLDER = "reprocess_merged_lambdas_kstyle";
+ private static final String MAIN_CLASS = "reprocess_merged_lambdas_kstyle.MainKt";
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public KotlinLambdaMergingDebugTest(TestParameters parameters) {
+ super(KotlinTargetVersion.JAVA_6);
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testMergingKStyleLambdasAndReprocessingInDebug() throws Exception {
+ testForR8(parameters.getBackend())
+ .setMode(CompilationMode.DEBUG)
+ .addProgramFiles(getKotlinJarFile(FOLDER))
+ .addProgramFiles(getJavaJarFile(FOLDER))
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(MAIN_CLASS)
+ .allowDiagnosticWarningMessages()
+ .compile()
+ .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."));
+ }
+}
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 8102aaf..2811374 100644
--- a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
@@ -107,12 +107,13 @@
"-assumemayhavesideeffects class adaptresourcefilenames.pkg.innerpkg.D {",
" void <init>();",
"}",
- "-neverclassinline class *");
+ "-neverclassinline class *",
+ "-nohorizontalclassmerging class adaptresourcefilenames.pkg.C",
+ "-nohorizontalclassmerging class adaptresourcefilenames.pkg.innerpkg.D");
}
@Test
public void testEnabled() throws Exception {
- expectThrowsWithHorizontalClassMerging();
DataResourceConsumerForTesting dataResourceConsumer = new DataResourceConsumerForTesting();
compileWithR8(
getProguardConfigWithNeverInline(true, null),
diff --git a/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java b/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
index 6ababf1..d56503c 100644
--- a/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
+++ b/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
@@ -63,7 +63,7 @@
@Test
public void test() throws Exception {
R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
+ testForR8Compat(parameters.getBackend())
.addProgramClasses(Main.class, Service.class, Foo.class, FooImpl.class)
.addKeepMainRule(Main.class)
.addKeepAttributes(
diff --git a/src/test/java/com/android/tools/r8/naming/b130791310/B130791310.java b/src/test/java/com/android/tools/r8/naming/b130791310/B130791310.java
index bb27506..23271e2 100644
--- a/src/test/java/com/android/tools/r8/naming/b130791310/B130791310.java
+++ b/src/test/java/com/android/tools/r8/naming/b130791310/B130791310.java
@@ -3,14 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming.b130791310;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
-import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.ProguardVersion;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
@@ -108,45 +107,27 @@
this.parameters = parameters;
}
- private void inspect(CodeInspector inspector, boolean isR8) {
+ private void inspect(CodeInspector inspector) {
ClassSubject holder = inspector.clazz(SomeLogic.class);
assertThat(holder, isPresentAndNotRenamed());
MethodSubject someMethod = holder.uniqueMethodWithName("someMethod");
- if (isR8) {
- if (onlyForceInlining) {
- assertThat(someMethod, isPresentAndNotRenamed());
- } else {
- assertThat(someMethod, not(isPresent()));
- }
- } else {
- if (enableClassMerging) {
- // Note that the method is not entirely gone, but merged to the implementer, along with some
- // method signature modification.
- assertThat(someMethod, not(isPresent()));
- } else {
- assertThat(someMethod, isPresentAndNotRenamed());
- }
- }
+ assertThat(someMethod, isPresentAndNotRenamed());
}
@Test
public void testProguard() throws Exception {
assumeFalse(onlyForceInlining);
assumeTrue(parameters.isCfRuntime());
- testForProguard()
+ testForProguard(ProguardVersion.getLatest())
.addProgramClasses(CLASSES)
.addKeepClassAndMembersRules(MAIN)
.addKeepRules(RULES)
.addTestingAnnotationsAsProgramClasses()
.setMinApi(parameters.getApiLevel())
- .apply(
- builder -> {
- if (!enableClassMerging) {
- builder.addKeepRules("-optimizations !class/merging/*");
- }
- })
+ .applyIf(
+ !enableClassMerging, builder -> builder.addKeepRules("-optimizations !class/merging/*"))
.compile()
- .inspect(inspector -> inspect(inspector, false));
+ .inspect(this::inspect);
}
@Test
@@ -158,14 +139,12 @@
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.addOptionsModification(o -> o.enableVerticalClassMerging = enableClassMerging)
- .apply(
- builder -> {
- if (onlyForceInlining) {
+ .applyIf(
+ onlyForceInlining,
+ builder ->
builder.addOptionsModification(
- o -> o.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE));
- }
- })
+ o -> o.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE)))
.compile()
- .inspect(inspector -> inspect(inspector, true));
+ .inspect(this::inspect);
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
index 0da2866..a64903b 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
@@ -16,6 +16,7 @@
import com.google.common.base.Equivalence;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.hamcrest.Description;
@@ -58,6 +59,11 @@
return this;
}
+ public Builder map(int i, Function<StackTraceLine, StackTraceLine> map) {
+ stackTraceLines.set(i, map.apply(stackTraceLines.get(i)));
+ return this;
+ }
+
public StackTrace build() {
return new StackTrace(
stackTraceLines,
@@ -123,6 +129,14 @@
this.lineNumber = lineNumber;
}
+ public Builder builderOf() {
+ return new Builder()
+ .setFileName(fileName)
+ .setClassName(className)
+ .setLineNumber(lineNumber)
+ .setMethodName(methodName);
+ }
+
public static Builder builder() {
return new Builder();
}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java b/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java
index e945182..65445a7 100644
--- a/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java
@@ -85,6 +85,7 @@
.addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
.addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
.addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
+ .addKeepAllClassesRuleWithAllowObfuscation()
.addKeepMainRule(Main.class)
.addProgramClasses(Main.class)
.addProgramClassesAndInnerClasses(A.class, B.class, CY.class, CYY.class)
diff --git a/src/test/java/com/android/tools/r8/regress/b149890887/MissingLibraryTargetTest.java b/src/test/java/com/android/tools/r8/regress/b149890887/MissingLibraryTargetTest.java
index 58e43469..927decb 100644
--- a/src/test/java/com/android/tools/r8/regress/b149890887/MissingLibraryTargetTest.java
+++ b/src/test/java/com/android/tools/r8/regress/b149890887/MissingLibraryTargetTest.java
@@ -62,7 +62,6 @@
@Test
public void testR8() throws Exception {
testForR8(parameters.getBackend())
- .allowDiagnosticWarningMessages(isDesugaring())
.addProgramClasses(PROGRAM)
.addKeepMainRule(MAIN)
.addClasspathClasses(LIBRARY)
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideInterfaceTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideInterfaceTest.java
new file mode 100644
index 0000000..4e8bdd8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideInterfaceTest.java
@@ -0,0 +1,92 @@
+// Copyright (c) 2020, 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.resolution.virtualtargets;
+
+import static org.hamcrest.CoreMatchers.containsString;
+
+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.TestParametersCollection;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.resolution.virtualtargets.package_a.ViewModel;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// This is a reproduction of b/171369796.
+@RunWith(Parameterized.class)
+public class PackagePrivateFinalOverrideInterfaceTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public PackagePrivateFinalOverrideInterfaceTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(ViewModel.class, I.class, Zoolander.class, Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::assertResult);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(ViewModel.class, I.class, Zoolander.class, Main.class)
+ .addKeepClassAndMembersRules(ViewModel.class)
+ .addKeepClassAndMembersRules(I.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::assertResult);
+ }
+
+ public void assertResult(TestRunResult<?> runResult) {
+ if (parameters.isDexRuntime()
+ && parameters.getRuntime().asDex().getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_HOST)) {
+ runResult.assertFailureWithErrorThatMatches(containsString("overrides final"));
+ } else {
+ runResult.assertSuccessWithOutputLines("Zoolander::clear()");
+ }
+ }
+
+ public interface I {
+ void clear();
+ }
+
+ @NeverClassInline
+ public static class Zoolander extends ViewModel implements I {
+
+ @Override
+ @NeverInline
+ public final void clear() {
+ System.out.println("Zoolander::clear()");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ runClear(args.length == 0 ? new Zoolander() : null);
+ }
+
+ public static void runClear(I i) {
+ i.clear();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
index b8b1647..6c43c35 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
@@ -63,6 +63,7 @@
@Test
public void matchMultipleTypeNamesOnLine() {
+ // TODO(b/171691292): We are not supporting having multiple class matches for.
runRetraceTest(
"%c\\s%c\\s%c",
new StackTraceForTest() {
@@ -79,7 +80,7 @@
@Override
public List<String> retracedStackTrace() {
- return ImmutableList.of("AA.AA.AA BB.BB.BB CC.CC.CC");
+ return ImmutableList.of("AA.AA.AA b.b.b c.c.c");
}
@Override
@@ -91,6 +92,7 @@
@Test
public void matchMultipleSlashNamesOnLine() {
+ // TODO(b/171691292): We are not supporting having multiple class matches for.
runRetraceTest(
"%C\\s%C\\s%C",
new StackTraceForTest() {
@@ -106,7 +108,7 @@
@Override
public List<String> retracedStackTrace() {
- return ImmutableList.of("AA/AA BB/BB CC/CC");
+ return ImmutableList.of("AA/AA b/b/b c/c/c");
}
@Override
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 edcb118..82d3915 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -10,7 +10,6 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
@@ -136,15 +135,11 @@
@Test
public void testAmbiguousStackTrace() {
- // TODO(b/170797525): Remove when we have a fixed ordering.
- assumeTrue(useRegExpParsing);
runRetraceTest(new AmbiguousStackTrace());
}
@Test
public void testAmbiguousMissingLineStackTrace() {
- // TODO(b/170797525): Remove when we have a fixed ordering.
- assumeTrue(useRegExpParsing);
runRetraceTest(new AmbiguousMissingLineStackTrace());
}
@@ -190,8 +185,6 @@
@Test
public void testUnknownSourceStackTrace() {
- // TODO(b/170797525): Remove when we have a fixed ordering.
- assumeTrue(useRegExpParsing);
runRetraceTest(new UnknownSourceStackTrace());
}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceVerboseTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceVerboseTests.java
index 3e5b4ca..2e75b1d 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceVerboseTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceVerboseTests.java
@@ -6,7 +6,7 @@
import static com.android.tools.r8.retrace.Retrace.DEFAULT_REGULAR_EXPRESSION;
import static junit.framework.TestCase.assertEquals;
-import static org.junit.Assume.assumeTrue;
+import static org.junit.Assume.assumeFalse;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
@@ -54,12 +54,12 @@
@Test
public void testAmbiguousMissingLineVerbose() {
+ // TODO(b/169346455): Enable when separated parser.
+ assumeFalse(useRegExpParsing);
runRetraceTest(new AmbiguousWithSignatureVerboseStackTrace());
}
private TestDiagnosticMessagesImpl runRetraceTest(StackTraceForTest stackTraceForTest) {
- // TODO(b/170293906): Remove assumption.
- assumeTrue(useRegExpParsing);
TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
RetraceCommand retraceCommand =
RetraceCommand.builder(diagnosticsHandler)
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousWithSignatureVerboseStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousWithSignatureVerboseStackTrace.java
index 1dfb3c9..45c103f 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousWithSignatureVerboseStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousWithSignatureVerboseStackTrace.java
@@ -33,10 +33,10 @@
return Arrays.asList(
"java.lang.IndexOutOfBoundsException",
"\tat java.util.ArrayList.get(ArrayList.java:411)",
- "\tat com.android.tools.r8.Internal.void foo(int)(Internal.java)",
- "\t<OR> at com.android.tools.r8.Internal.void foo(int,int)(Internal.java)",
+ "\tat com.android.tools.r8.Internal.boolean foo(int,int)(Internal.java)",
+ "\t<OR> at com.android.tools.r8.Internal.void foo(int)(Internal.java)",
"\t<OR> at com.android.tools.r8.Internal.void foo(int,boolean)(Internal.java)",
- "\t<OR> at com.android.tools.r8.Internal.boolean foo(int,int)(Internal.java)");
+ "\t<OR> at com.android.tools.r8.Internal.void foo(int,int)(Internal.java)");
}
@Override
diff --git a/src/test/java/com/android/tools/r8/shaking/EventuallyNonTargetedMethodTest.java b/src/test/java/com/android/tools/r8/shaking/EventuallyNonTargetedMethodTest.java
index b85fe68..24d4436 100644
--- a/src/test/java/com/android/tools/r8/shaking/EventuallyNonTargetedMethodTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/EventuallyNonTargetedMethodTest.java
@@ -8,6 +8,7 @@
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;
@@ -37,10 +38,10 @@
@Test
public void test() throws Exception {
- expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.enableInliningAnnotations()
.enableNoVerticalClassMergingAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.enableNeverClassInliningAnnotations()
.addInnerClasses(EventuallyNonTargetedMethodTest.class)
.addKeepMainRule(Main.class)
@@ -72,6 +73,7 @@
}
@NeverClassInline
+ @NoHorizontalClassMerging
private static class C extends A {
// Non-targeted override.
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
index 184fdc9..fd676a3 100644
--- a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
@@ -183,7 +183,6 @@
@Test
public void testConditionalEqualsKeepClassMembers() throws Exception {
- expectThrowsWithHorizontalClassMerging();
GraphInspector referenceInspector =
testForR8(Backend.CF)
.enableGraphInspector()
diff --git a/src/test/java/com/android/tools/r8/shaking/NonTargetedMethodTest.java b/src/test/java/com/android/tools/r8/shaking/NonTargetedMethodTest.java
index 42dab36..2b2e440 100644
--- a/src/test/java/com/android/tools/r8/shaking/NonTargetedMethodTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/NonTargetedMethodTest.java
@@ -9,6 +9,7 @@
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;
@@ -38,10 +39,10 @@
@Test
public void test() throws Exception {
- expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.enableInliningAnnotations()
.enableNoVerticalClassMergingAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.enableNeverClassInliningAnnotations()
.addInnerClasses(NonTargetedMethodTest.class)
.addKeepMainRule(Main.class)
@@ -72,6 +73,7 @@
}
@NeverClassInline
+ @NoHorizontalClassMerging
private static class C extends A {
// Non-targeted override.
diff --git a/src/test/java/com/android/tools/r8/shaking/allowshrinking/ConditionalKeepClassMethodsAllowShrinkingCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/allowshrinking/ConditionalKeepClassMethodsAllowShrinkingCompatibilityTest.java
new file mode 100644
index 0000000..f7e50d9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/allowshrinking/ConditionalKeepClassMethodsAllowShrinkingCompatibilityTest.java
@@ -0,0 +1,147 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking.allowshrinking;
+
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
+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.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ConditionalKeepClassMethodsAllowShrinkingCompatibilityTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final boolean allowOptimization;
+ private final boolean allowObfuscation;
+ private final Shrinker shrinker;
+
+ @Parameterized.Parameters(name = "{0}, opt:{1}, obf:{2}, {3}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(),
+ BooleanUtils.values(),
+ BooleanUtils.values(),
+ ImmutableList.of(Shrinker.R8, Shrinker.PG));
+ }
+
+ public ConditionalKeepClassMethodsAllowShrinkingCompatibilityTest(
+ TestParameters parameters,
+ boolean allowOptimization,
+ boolean allowObfuscation,
+ Shrinker shrinker) {
+ this.parameters = parameters;
+ this.allowOptimization = allowOptimization;
+ this.allowObfuscation = allowObfuscation;
+ this.shrinker = shrinker;
+ }
+
+ String getExpected() {
+ return StringUtils.lines(
+ "A::foo",
+ // Reflective lookup of A::foo will only work if optimization and obfuscation are disabled.
+ Boolean.toString(!allowOptimization && !allowObfuscation),
+ "false");
+ }
+
+ @Test
+ public void test() throws Exception {
+ if (shrinker.isR8()) {
+ run(testForR8(parameters.getBackend()));
+ } else {
+ run(testForProguard(shrinker.getProguardVersion()).addDontWarn(getClass()));
+ }
+ }
+
+ public <T extends TestShrinkerBuilder<?, ?, ?, ?, T>> void run(T builder) throws Exception {
+ String keepRule =
+ "-if class * -keepclassmembers,allowshrinking"
+ + (allowOptimization ? ",allowoptimization" : "")
+ + (allowObfuscation ? ",allowobfuscation" : "")
+ + " class <1> { java.lang.String foo(); java.lang.String bar(); }";
+ builder
+ .addInnerClasses(ConditionalKeepClassMethodsAllowShrinkingCompatibilityTest.class)
+ .addKeepClassAndMembersRules(TestClass.class)
+ .addKeepRules(keepRule)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class, A.class.getTypeName())
+ .assertSuccessWithOutput(getExpected())
+ .inspect(
+ inspector -> {
+ ClassSubject aClass = inspector.clazz(A.class);
+ ClassSubject bClass = inspector.clazz(B.class);
+ // The class constants will force A and B to be retained, but not the methods.
+ assertThat(bClass, isPresentAndRenamed());
+ assertThat(bClass.uniqueMethodWithName("foo"), not(isPresent()));
+ assertThat(bClass.uniqueMethodWithName("bar"), not(isPresent()));
+
+ assertThat(aClass, isPresentAndRenamed());
+ // The dependent rule with soft-pinning of bar never causes A::bar to be retained
+ // regardless of A and A::foo being retained.
+ assertThat(aClass.uniqueMethodWithName("bar"), not(isPresent()));
+ MethodSubject aFoo = aClass.uniqueMethodWithName("foo");
+ if (allowOptimization) {
+ assertThat(aFoo, not(isPresent()));
+ } else {
+ assertThat(aFoo, isPresentAndRenamed(allowObfuscation));
+ assertThat(inspector.clazz(TestClass.class).mainMethod(), invokesMethod(aFoo));
+ }
+ });
+ }
+
+ static class A {
+ public String foo() {
+ return "A::foo";
+ }
+
+ public String bar() {
+ return "A::bar";
+ }
+ }
+
+ static class B {
+ public String foo() {
+ return "B::foo";
+ }
+
+ public String bar() {
+ return "B::bar";
+ }
+ }
+
+ static class TestClass {
+
+ public static boolean hasFoo(String name) {
+ try {
+ return Class.forName(name).getDeclaredMethod("foo") != null;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public static void main(String[] args) {
+ // Direct call to A.foo, if optimization is not allowed it will be kept.
+ A a = new A();
+ System.out.println(a.foo());
+ // Reference to A should not retain A::foo when allowoptimization is set.
+ // Note: if using class constant A.class, PG will actually retain A::foo !?
+ System.out.println(hasFoo(a.getClass().getTypeName()));
+ // Reference to B should not retain B::foo regardless of allowoptimization.
+ System.out.println(hasFoo(B.class.getTypeName()));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/proguard/AllowShrinkingCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/allowshrinking/ConditionalKeepStaticMethodAllowShrinkingCompatibilityTest.java
similarity index 77%
copy from src/test/java/com/android/tools/r8/proguard/AllowShrinkingCompatibilityTest.java
copy to src/test/java/com/android/tools/r8/shaking/allowshrinking/ConditionalKeepStaticMethodAllowShrinkingCompatibilityTest.java
index ca30a1f..5c99afd 100644
--- a/src/test/java/com/android/tools/r8/proguard/AllowShrinkingCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/allowshrinking/ConditionalKeepStaticMethodAllowShrinkingCompatibilityTest.java
@@ -2,7 +2,7 @@
// 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.proguard;
+package com.android.tools.r8.shaking.allowshrinking;
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -10,13 +10,14 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.ProguardVersion;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -24,39 +25,48 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class AllowShrinkingCompatibilityTest extends TestBase {
+public class ConditionalKeepStaticMethodAllowShrinkingCompatibilityTest extends TestBase {
private final boolean allowOptimization;
private final TestParameters parameters;
- private final ProguardVersion proguardVersion;
+ private final Shrinker shrinker;
@Parameters(name = "{1}, {2}, allow optimization: {0}")
public static List<Object[]> data() {
return buildParameters(
BooleanUtils.values(),
getTestParameters().withCfRuntimes().build(),
- ProguardVersion.values());
+ ImmutableList.of(Shrinker.R8, Shrinker.PG));
}
- public AllowShrinkingCompatibilityTest(
- boolean allowOptimization, TestParameters parameters, ProguardVersion proguardVersion) {
+ public ConditionalKeepStaticMethodAllowShrinkingCompatibilityTest(
+ boolean allowOptimization, TestParameters parameters, Shrinker shrinker) {
this.allowOptimization = allowOptimization;
this.parameters = parameters;
- this.proguardVersion = proguardVersion;
+ this.shrinker = shrinker;
}
@Test
public void test() throws Exception {
- testForProguard(proguardVersion)
+ if (shrinker.isPG()) {
+ run(testForProguard(shrinker.getProguardVersion()).addDontWarn(getClass()));
+ } else {
+ run(testForR8(parameters.getBackend()));
+ }
+ }
+
+ private <T extends TestShrinkerBuilder<?, ?, ?, ?, T>> void run(T builder) throws Exception {
+ builder
.addProgramClasses(TestClass.class, Companion.class)
.addKeepMainRule(TestClass.class)
.addKeepRules(
- "-keep,allowshrinking"
+ "-if class "
+ + Companion.class.getTypeName()
+ + " -keep,allowshrinking"
+ (allowOptimization ? ",allowoptimization" : "")
+ " class "
+ Companion.class.getTypeName()
+ " { <methods>; }")
- .addDontWarn(getClass())
.compile()
.inspect(
inspector -> {
diff --git a/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepAllowShrinkingCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepAllowShrinkingCompatibilityTest.java
new file mode 100644
index 0000000..c9b3bc7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepAllowShrinkingCompatibilityTest.java
@@ -0,0 +1,137 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking.allowshrinking;
+
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
+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.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KeepAllowShrinkingCompatibilityTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final boolean allowOptimization;
+ private final boolean allowObfuscation;
+ private final Shrinker shrinker;
+
+ @Parameterized.Parameters(name = "{0}, opt:{1}, obf:{2}, {3}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(),
+ BooleanUtils.values(),
+ BooleanUtils.values(),
+ ImmutableList.of(Shrinker.R8, Shrinker.PG));
+ }
+
+ public KeepAllowShrinkingCompatibilityTest(
+ TestParameters parameters,
+ boolean allowOptimization,
+ boolean allowObfuscation,
+ Shrinker shrinker) {
+ this.parameters = parameters;
+ this.allowOptimization = allowOptimization;
+ this.allowObfuscation = allowObfuscation;
+ this.shrinker = shrinker;
+ }
+
+ String getExpected() {
+ return StringUtils.lines(
+ "A::foo",
+ // Reflective lookup of A::foo will only work if optimization and obfuscation are disabled.
+ Boolean.toString(!allowOptimization && !allowObfuscation),
+ "false");
+ }
+
+ @Test
+ public void test() throws Exception {
+ if (shrinker.isR8()) {
+ run(
+ testForR8(parameters.getBackend())
+ // Allowing all of shrinking, optimization and obfuscation will amount to a nop rule.
+ .allowUnusedProguardConfigurationRules(allowOptimization && allowObfuscation));
+ } else {
+ run(testForProguard(shrinker.getProguardVersion()).addDontWarn(getClass()));
+ }
+ }
+
+ public <T extends TestShrinkerBuilder<?, ?, ?, ?, T>> void run(T builder) throws Exception {
+ String keepRule =
+ "-keep,allowshrinking"
+ + (allowOptimization ? ",allowoptimization" : "")
+ + (allowObfuscation ? ",allowobfuscation" : "")
+ + " class * { java.lang.String foo(); }";
+ builder
+ .addInnerClasses(KeepAllowShrinkingCompatibilityTest.class)
+ .addKeepClassAndMembersRules(TestClass.class)
+ .addKeepRules(keepRule)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class, A.class.getTypeName())
+ .assertSuccessWithOutput(getExpected())
+ .inspect(
+ inspector -> {
+ ClassSubject aClass = inspector.clazz(A.class);
+ ClassSubject bClass = inspector.clazz(B.class);
+ // The class constants will force A and B to be retained, but not the foo methods.
+ assertThat(bClass, isPresentAndRenamed(allowObfuscation));
+ assertThat(aClass, isPresentAndRenamed(allowObfuscation));
+ assertThat(bClass.uniqueMethodWithName("foo"), not(isPresent()));
+ MethodSubject aFoo = aClass.uniqueMethodWithName("foo");
+ if (allowOptimization) {
+ assertThat(aFoo, not(isPresent()));
+ } else {
+ assertThat(aFoo, isPresentAndRenamed(allowObfuscation));
+ assertThat(inspector.clazz(TestClass.class).mainMethod(), invokesMethod(aFoo));
+ }
+ });
+ }
+
+ static class A {
+ public String foo() {
+ return "A::foo";
+ }
+ }
+
+ static class B {
+ public String foo() {
+ return "B::foo";
+ }
+ }
+
+ static class TestClass {
+
+ public static boolean hasFoo(String name) {
+ try {
+ return Class.forName(name).getDeclaredMethod("foo") != null;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public static void main(String[] args) {
+ // Direct call to A.foo, if optimization is not allowed it will be kept.
+ A a = new A();
+ System.out.println(a.foo());
+ // Reference to A should not retain A::foo when allowoptimization is set.
+ // Note: if using class constant A.class, PG will actually retain A::foo !?
+ System.out.println(hasFoo(a.getClass().getTypeName()));
+ // Reference to B should not retain B::foo regardless of allowoptimization.
+ System.out.println(hasFoo(B.class.getTypeName()));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepAllowShrinkingIncludeDescriptorClassesCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepAllowShrinkingIncludeDescriptorClassesCompatibilityTest.java
new file mode 100644
index 0000000..44abb34
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepAllowShrinkingIncludeDescriptorClassesCompatibilityTest.java
@@ -0,0 +1,120 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.allowshrinking;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+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 KeepAllowShrinkingIncludeDescriptorClassesCompatibilityTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final Shrinker shrinker;
+
+ @Parameters(name = "{0}, {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(), ImmutableList.of(Shrinker.R8, Shrinker.PG));
+ }
+
+ public KeepAllowShrinkingIncludeDescriptorClassesCompatibilityTest(
+ TestParameters parameters, Shrinker shrinker) {
+ this.parameters = parameters;
+ this.shrinker = shrinker;
+ }
+
+ @Test
+ public void test() throws Exception {
+ if (shrinker.isPG()) {
+ run(testForProguard(shrinker.getProguardVersion()).addDontWarn(getClass()));
+ } else {
+ run(testForR8(parameters.getBackend()));
+ }
+ }
+
+ private <T extends TestShrinkerBuilder<?, ?, ?, ?, T>> void run(T builder) throws Exception {
+ builder
+ .addProgramClasses(TestClass.class, A.class, B.class, SoftPinned.class)
+ .addKeepClassAndMembersRules(TestClass.class)
+ .addKeepRules(
+ "-keepclassmembers,allowshrinking,includedescriptorclasses class "
+ + SoftPinned.class.getTypeName()
+ + " { <methods>; }")
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("true")
+ .inspect(
+ inspector -> {
+ assertThat(inspector.clazz(TestClass.class), isPresent());
+
+ ClassSubject softPinnedClass = inspector.clazz(SoftPinned.class);
+ assertThat(softPinnedClass.uniqueMethodWithName("used"), isPresentAndNotRenamed());
+ assertThat(softPinnedClass.uniqueMethodWithName("unused"), not(isPresent()));
+
+ // SoftPinned.used(A) remains thus A must be present and not renamed.
+ assertThat(inspector.clazz(A.class), isPresentAndNotRenamed());
+
+ // TODO(b/171548534): Unexpectedly the behavior here is that the class B is also not
+ // renamed. It appears that both R8 and PG will eagerly mark the name as not needing
+ // to be renamed.
+ assertThat(inspector.clazz(B.class), isPresentAndNotRenamed());
+ });
+ }
+
+ static class A {
+ @Override
+ public String toString() {
+ return getClass().getTypeName();
+ }
+ }
+
+ static class B {
+ @Override
+ public String toString() {
+ return getClass().getTypeName();
+ }
+ }
+
+ static class SoftPinned {
+
+ public static void used(A a) {
+ System.out.println(a.toString().endsWith(System.nanoTime() > 0 ? "A" : "junk"));
+ }
+
+ public static void unused(B b) {
+ System.out.println(b.toString().endsWith(System.nanoTime() > 0 ? "B" : "junk"));
+ }
+ }
+
+ static class TestClass {
+
+ // Kept helper so the classes A and B escape and prohibit removal.
+ public static void kept(Object o) {
+ if (System.nanoTime() < 0) {
+ System.out.println(o.toString());
+ }
+ }
+
+ public static void main(String[] args) {
+ A a = new A();
+ B b = new B();
+ kept(a);
+ kept(b);
+ SoftPinned.used(a);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepClassFieldsAllowShrinkingCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepClassFieldsAllowShrinkingCompatibilityTest.java
new file mode 100644
index 0000000..8b01419
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepClassFieldsAllowShrinkingCompatibilityTest.java
@@ -0,0 +1,158 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking.allowshrinking;
+
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.accessesField;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
+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.FieldSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KeepClassFieldsAllowShrinkingCompatibilityTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final boolean allowOptimization;
+ private final boolean allowObfuscation;
+ private final Shrinker shrinker;
+
+ @Parameterized.Parameters(name = "{0}, opt:{1}, obf:{2}, {3}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(),
+ BooleanUtils.values(),
+ BooleanUtils.values(),
+ ImmutableList.of(Shrinker.R8, Shrinker.PG));
+ }
+
+ public KeepClassFieldsAllowShrinkingCompatibilityTest(
+ TestParameters parameters,
+ boolean allowOptimization,
+ boolean allowObfuscation,
+ Shrinker shrinker) {
+ this.parameters = parameters;
+ this.allowOptimization = allowOptimization;
+ this.allowObfuscation = allowObfuscation;
+ this.shrinker = shrinker;
+ }
+
+ String getExpected() {
+ return StringUtils.lines(
+ "A.foo",
+ // R8 will succeed in removing the field if allowoptimization is set.
+ Boolean.toString((shrinker.isPG() || !allowOptimization) && !allowObfuscation),
+ // R8 will always remove the unreferenced B.foo field.
+ Boolean.toString(shrinker.isPG() && !allowOptimization && !allowObfuscation));
+ }
+
+ @Test
+ public void test() throws Exception {
+ if (shrinker.isR8()) {
+ run(
+ testForR8(parameters.getBackend())
+ // Allowing all of shrinking, optimization and obfuscation will amount to a nop rule.
+ .allowUnusedProguardConfigurationRules(allowOptimization && allowObfuscation));
+ } else {
+ run(testForProguard(shrinker.getProguardVersion()).addDontWarn(getClass()));
+ }
+ }
+
+ public <T extends TestShrinkerBuilder<?, ?, ?, ?, T>> void run(T builder) throws Exception {
+ String keepRule =
+ "-keepclassmembers,allowshrinking"
+ + (allowOptimization ? ",allowoptimization" : "")
+ + (allowObfuscation ? ",allowobfuscation" : "")
+ + " class * { java.lang.String foo; java.lang.String bar; }";
+ builder
+ .addInnerClasses(KeepClassFieldsAllowShrinkingCompatibilityTest.class)
+ .addKeepClassAndMembersRules(TestClass.class)
+ .addKeepRules(keepRule)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class, A.class.getTypeName())
+ .assertSuccessWithOutput(getExpected())
+ .inspect(
+ inspector -> {
+ ClassSubject aClass = inspector.clazz(A.class);
+ ClassSubject bClass = inspector.clazz(B.class);
+ // The class constants will force A and B to be retained but renamed.
+ assertThat(aClass, isPresentAndRenamed());
+ assertThat(bClass, isPresentAndRenamed());
+
+ FieldSubject aFoo = aClass.uniqueFieldWithName("foo");
+ FieldSubject aBar = aClass.uniqueFieldWithName("bar");
+ FieldSubject bFoo = bClass.uniqueFieldWithName("foo");
+ FieldSubject bBar = bClass.uniqueFieldWithName("bar");
+
+ if (allowOptimization) {
+ // PG fails to optimize out the referenced field.
+ assertThat(aFoo, notIf(isPresent(), shrinker.isR8()));
+ assertThat(aBar, not(isPresent()));
+ assertThat(bFoo, not(isPresent()));
+ assertThat(bBar, not(isPresent()));
+ } else {
+ assertThat(aFoo, isPresentAndRenamed(allowObfuscation));
+ // TODO(b/171459868) It is inconsistent that the unused field A.bar is retained.
+ // This does not match the R8 behavior for an unused method, so there may be an
+ // optimization opportunity here.
+ // (See KeepClassMethodsAllowShrinkingCompatibilityTest regarding methods).
+ assertThat(aBar, isPresentAndRenamed(allowObfuscation));
+ assertThat(inspector.clazz(TestClass.class).mainMethod(), accessesField(aFoo));
+ if (shrinker.isR8()) {
+ assertThat(bFoo, not(isPresent()));
+ assertThat(bBar, not(isPresent()));
+ } else {
+ assertThat(bFoo, isPresentAndRenamed(allowObfuscation));
+ assertThat(bBar, isPresentAndRenamed(allowObfuscation));
+ }
+ }
+ });
+ }
+
+ static class A {
+ // Note: If the fields are final PG actually allows itself to inline the values.
+ public String foo = "A.foo";
+ public String bar = "A.bar";
+ }
+
+ static class B {
+ public String foo = "B.foo";
+ public String bar = "B.bar";
+ }
+
+ static class TestClass {
+
+ public static boolean hasFoo(String name) {
+ try {
+ return Class.forName(name).getDeclaredField("foo") != null;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public static void main(String[] args) {
+ // Conditional instance to prohibit class inlining of A.
+ A a = args.length == 42 ? null : new A();
+ // Direct use of A.foo, if optimization is not allowed it will be kept.
+ System.out.println(a.foo);
+ // Reference to A should not retain A.foo when allowoptimization is set.
+ System.out.println(hasFoo(a.getClass().getTypeName()));
+ // Reference to B should not retain B.foo regardless of allowoptimization.
+ System.out.println(hasFoo(B.class.getTypeName()));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepClassMethodsAllowShrinkingCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepClassMethodsAllowShrinkingCompatibilityTest.java
new file mode 100644
index 0000000..e7d5191
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepClassMethodsAllowShrinkingCompatibilityTest.java
@@ -0,0 +1,150 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking.allowshrinking;
+
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
+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.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KeepClassMethodsAllowShrinkingCompatibilityTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final boolean allowOptimization;
+ private final boolean allowObfuscation;
+ private final Shrinker shrinker;
+
+ @Parameterized.Parameters(name = "{0}, opt:{1}, obf:{2}, {3}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(),
+ BooleanUtils.values(),
+ BooleanUtils.values(),
+ ImmutableList.of(Shrinker.R8, Shrinker.PG));
+ }
+
+ public KeepClassMethodsAllowShrinkingCompatibilityTest(
+ TestParameters parameters,
+ boolean allowOptimization,
+ boolean allowObfuscation,
+ Shrinker shrinker) {
+ this.parameters = parameters;
+ this.allowOptimization = allowOptimization;
+ this.allowObfuscation = allowObfuscation;
+ this.shrinker = shrinker;
+ }
+
+ String getExpected() {
+ return StringUtils.lines(
+ "A::foo",
+ // Reflective lookup of A::foo will only work if optimization and obfuscation are disabled.
+ Boolean.toString(!allowOptimization && !allowObfuscation),
+ "false");
+ }
+
+ @Test
+ public void test() throws Exception {
+ if (shrinker.isR8()) {
+ run(
+ testForR8(parameters.getBackend())
+ // Allowing all of shrinking, optimization and obfuscation will amount to a nop rule.
+ .allowUnusedProguardConfigurationRules(allowOptimization && allowObfuscation));
+ } else {
+ run(testForProguard(shrinker.getProguardVersion()).addDontWarn(getClass()));
+ }
+ }
+
+ public <T extends TestShrinkerBuilder<?, ?, ?, ?, T>> void run(T builder) throws Exception {
+ String keepRule =
+ "-keepclassmembers,allowshrinking"
+ + (allowOptimization ? ",allowoptimization" : "")
+ + (allowObfuscation ? ",allowobfuscation" : "")
+ + " class * { java.lang.String foo(); java.lang.String bar(); }";
+ builder
+ .addInnerClasses(KeepClassMethodsAllowShrinkingCompatibilityTest.class)
+ .addKeepClassAndMembersRules(TestClass.class)
+ .addKeepRules(keepRule)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class, A.class.getTypeName())
+ .assertSuccessWithOutput(getExpected())
+ .inspect(
+ inspector -> {
+ ClassSubject aClass = inspector.clazz(A.class);
+ ClassSubject bClass = inspector.clazz(B.class);
+ // The class constants will force A and B to be retained, but not the methods.
+ assertThat(bClass, isPresentAndRenamed());
+ assertThat(bClass.uniqueMethodWithName("foo"), not(isPresent()));
+ assertThat(bClass.uniqueMethodWithName("bar"), not(isPresent()));
+
+ assertThat(aClass, isPresentAndRenamed());
+ // The dependent rule with soft-pinning of bar never causes A::bar to be retained
+ // regardless of A and A::foo being retained.
+ assertThat(aClass.uniqueMethodWithName("bar"), not(isPresent()));
+ MethodSubject aFoo = aClass.uniqueMethodWithName("foo");
+ if (allowOptimization) {
+ assertThat(aFoo, not(isPresent()));
+ } else {
+ assertThat(aFoo, isPresentAndRenamed(allowObfuscation));
+ assertThat(inspector.clazz(TestClass.class).mainMethod(), invokesMethod(aFoo));
+ }
+ });
+ }
+
+ static class A {
+ public String foo() {
+ return "A::foo";
+ }
+
+ public String bar() {
+ return "A::bar";
+ }
+ }
+
+ static class B {
+ public String foo() {
+ return "B::foo";
+ }
+
+ public String bar() {
+ return "B::bar";
+ }
+ }
+
+ static class TestClass {
+
+ public static boolean hasFoo(String name) {
+ try {
+ return Class.forName(name).getDeclaredMethod("foo") != null;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public static void main(String[] args) {
+ // Direct call to A.foo, if optimization is not allowed it will be kept.
+ A a = new A();
+ System.out.println(a.foo());
+ // Reference to A should not retain A::foo when allowoptimization is set.
+ // Note: if using class constant A.class, PG will actually retain A::foo !?
+ System.out.println(hasFoo(a.getClass().getTypeName()));
+ // Reference to B should not retain B::foo regardless of allowoptimization.
+ System.out.println(hasFoo(B.class.getTypeName()));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepMethodNameCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepMethodNameCompatibilityTest.java
new file mode 100644
index 0000000..4266e57
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepMethodNameCompatibilityTest.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.allowshrinking;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.ProguardVersion;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestShrinkerBuilder;
+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 KeepMethodNameCompatibilityTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public KeepMethodNameCompatibilityTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testProguard() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ test(testForProguard(ProguardVersion.getLatest()).addDontWarn(getClass()));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ test(testForR8(parameters.getBackend()));
+ }
+
+ private <T extends TestShrinkerBuilder<?, ?, ?, ?, T>> void test(T testBuilder) throws Exception {
+ testBuilder
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addKeepRules(
+ "-keepclassmembernames class " + TestClass.class.getTypeName() + " { void test(); }")
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector ->
+ assertThat(
+ inspector.clazz(TestClass.class).uniqueMethodWithName("test"), isPresent()))
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("foo");
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ test();
+ }
+
+ static void test() {
+ System.out.println("foo");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/proguard/AllowShrinkingCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepStaticFieldAllowShrinkingCompatibilityTest.java
similarity index 64%
copy from src/test/java/com/android/tools/r8/proguard/AllowShrinkingCompatibilityTest.java
copy to src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepStaticFieldAllowShrinkingCompatibilityTest.java
index ca30a1f..2322f85 100644
--- a/src/test/java/com/android/tools/r8/proguard/AllowShrinkingCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepStaticFieldAllowShrinkingCompatibilityTest.java
@@ -2,21 +2,23 @@
// 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.proguard;
+package com.android.tools.r8.shaking.allowshrinking;
-import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.accessesField;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.ProguardVersion;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -24,30 +26,38 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class AllowShrinkingCompatibilityTest extends TestBase {
+public class KeepStaticFieldAllowShrinkingCompatibilityTest extends TestBase {
private final boolean allowOptimization;
private final TestParameters parameters;
- private final ProguardVersion proguardVersion;
+ private final Shrinker shrinker;
@Parameters(name = "{1}, {2}, allow optimization: {0}")
public static List<Object[]> data() {
return buildParameters(
BooleanUtils.values(),
getTestParameters().withCfRuntimes().build(),
- ProguardVersion.values());
+ ImmutableList.of(Shrinker.R8, Shrinker.PG));
}
- public AllowShrinkingCompatibilityTest(
- boolean allowOptimization, TestParameters parameters, ProguardVersion proguardVersion) {
+ public KeepStaticFieldAllowShrinkingCompatibilityTest(
+ boolean allowOptimization, TestParameters parameters, Shrinker shrinker) {
this.allowOptimization = allowOptimization;
this.parameters = parameters;
- this.proguardVersion = proguardVersion;
+ this.shrinker = shrinker;
}
@Test
public void test() throws Exception {
- testForProguard(proguardVersion)
+ if (shrinker.isPG()) {
+ run(testForProguard(shrinker.getProguardVersion()).addDontWarn(getClass()));
+ } else {
+ run(testForR8(parameters.getBackend()));
+ }
+ }
+
+ private <T extends TestShrinkerBuilder<?, ?, ?, ?, T>> void run(T builder) throws Exception {
+ builder
.addProgramClasses(TestClass.class, Companion.class)
.addKeepMainRule(TestClass.class)
.addKeepRules(
@@ -55,29 +65,29 @@
+ (allowOptimization ? ",allowoptimization" : "")
+ " class "
+ Companion.class.getTypeName()
- + " { <methods>; }")
- .addDontWarn(getClass())
+ + " { <fields>; }")
.compile()
.inspect(
inspector -> {
ClassSubject testClassSubject = inspector.clazz(TestClass.class);
assertThat(testClassSubject, isPresent());
- ClassSubject companionClassSubject = inspector.clazz(Companion.class);
- assertThat(companionClassSubject, notIf(isPresent(), allowOptimization));
-
MethodSubject mainMethodSubject = testClassSubject.mainMethod();
- MethodSubject getMethodSubject = companionClassSubject.uniqueMethodWithName("get");
+ ClassSubject companionClassSubject = inspector.clazz(Companion.class);
+ FieldSubject xFieldSubject = companionClassSubject.uniqueFieldWithName("x");
- if (allowOptimization) {
+ // PG fails to optimize fields regardless of keep flags.
+ if (allowOptimization && shrinker.isR8()) {
+ assertThat(companionClassSubject, not(isPresent()));
assertTrue(
testClassSubject
.mainMethod()
.streamInstructions()
.allMatch(InstructionSubject::isReturnVoid));
} else {
- assertThat(mainMethodSubject, invokesMethod(getMethodSubject));
- assertThat(getMethodSubject, isPresent());
+ assertThat(companionClassSubject, isPresent());
+ assertThat(mainMethodSubject, accessesField(xFieldSubject));
+ assertThat(xFieldSubject, isPresent());
}
})
.run(parameters.getRuntime(), TestClass.class)
@@ -87,7 +97,7 @@
static class TestClass {
public static void main(String[] args) {
- if (Companion.get() != 42) {
+ if (Companion.x != 42) {
System.out.println("Hello world!");
}
}
@@ -95,8 +105,6 @@
static class Companion {
- static int get() {
- return 42;
- }
+ static int x = 42;
}
}
diff --git a/src/test/java/com/android/tools/r8/proguard/AllowShrinkingCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepStaticMethodAllowShrinkingCompatibilityTest.java
similarity index 80%
rename from src/test/java/com/android/tools/r8/proguard/AllowShrinkingCompatibilityTest.java
rename to src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepStaticMethodAllowShrinkingCompatibilityTest.java
index ca30a1f..9dbdbb4 100644
--- a/src/test/java/com/android/tools/r8/proguard/AllowShrinkingCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepStaticMethodAllowShrinkingCompatibilityTest.java
@@ -2,7 +2,7 @@
// 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.proguard;
+package com.android.tools.r8.shaking.allowshrinking;
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -10,13 +10,14 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.ProguardVersion;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -24,30 +25,38 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class AllowShrinkingCompatibilityTest extends TestBase {
+public class KeepStaticMethodAllowShrinkingCompatibilityTest extends TestBase {
private final boolean allowOptimization;
private final TestParameters parameters;
- private final ProguardVersion proguardVersion;
+ private final Shrinker shrinker;
@Parameters(name = "{1}, {2}, allow optimization: {0}")
public static List<Object[]> data() {
return buildParameters(
BooleanUtils.values(),
getTestParameters().withCfRuntimes().build(),
- ProguardVersion.values());
+ ImmutableList.of(Shrinker.R8, Shrinker.PG));
}
- public AllowShrinkingCompatibilityTest(
- boolean allowOptimization, TestParameters parameters, ProguardVersion proguardVersion) {
+ public KeepStaticMethodAllowShrinkingCompatibilityTest(
+ boolean allowOptimization, TestParameters parameters, Shrinker shrinker) {
this.allowOptimization = allowOptimization;
this.parameters = parameters;
- this.proguardVersion = proguardVersion;
+ this.shrinker = shrinker;
}
@Test
public void test() throws Exception {
- testForProguard(proguardVersion)
+ if (shrinker.isPG()) {
+ run(testForProguard(shrinker.getProguardVersion()).addDontWarn(getClass()));
+ } else {
+ run(testForR8(parameters.getBackend()));
+ }
+ }
+
+ private <T extends TestShrinkerBuilder<?, ?, ?, ?, T>> void run(T builder) throws Exception {
+ builder
.addProgramClasses(TestClass.class, Companion.class)
.addKeepMainRule(TestClass.class)
.addKeepRules(
@@ -56,7 +65,6 @@
+ " class "
+ Companion.class.getTypeName()
+ " { <methods>; }")
- .addDontWarn(getClass())
.compile()
.inspect(
inspector -> {
diff --git a/src/test/java/com/android/tools/r8/shaking/allowshrinking/Shrinker.java b/src/test/java/com/android/tools/r8/shaking/allowshrinking/Shrinker.java
new file mode 100644
index 0000000..112fd05
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/allowshrinking/Shrinker.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking.allowshrinking;
+
+import com.android.tools.r8.ProguardVersion;
+
+class Shrinker {
+
+ public static final Shrinker R8 = new Shrinker(null);
+ public static final Shrinker PG = new Shrinker(ProguardVersion.getLatest());
+ public static final Shrinker PG5 = new Shrinker(ProguardVersion.V5_2_1);
+ public static final Shrinker PG6 = new Shrinker(ProguardVersion.V6_0_1);
+ public static final Shrinker PG7 = new Shrinker(ProguardVersion.V7_0_0);
+
+ private final ProguardVersion proguardVersion;
+
+ private Shrinker(ProguardVersion proguardVersion) {
+ this.proguardVersion = proguardVersion;
+ }
+
+ boolean isR8() {
+ return proguardVersion == null;
+ }
+
+ boolean isPG() {
+ return proguardVersion != null;
+ }
+
+ public ProguardVersion getProguardVersion() {
+ return proguardVersion;
+ }
+
+ @Override
+ public String toString() {
+ return isR8() ? "r8" : "pg" + proguardVersion.getVersion();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnFieldsTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnFieldsTest.java
index d0e3b9e..55556b4 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnFieldsTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.AssumeMayHaveSideEffects;
import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -45,7 +46,6 @@
@Test
public void test() throws Exception {
- expectThrowsWithHorizontalClassMerging();
testForR8Compat(backend)
.enableNeverClassInliningAnnotations()
.addProgramClasses(CLASSES)
@@ -55,6 +55,7 @@
"-keepclassmembers class * { @**.*Annotation <fields>; }",
"-keepattributes *Annotation*")
.enableSideEffectAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.compile()
.inspect(
inspector -> {
@@ -92,6 +93,8 @@
}
class FieldAnnotationUse {}
+
+@NoHorizontalClassMerging
class StaticFieldAnnotationUse {}
@NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsForJavaLangClassTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsForJavaLangClassTest.java
new file mode 100644
index 0000000..eb29f88
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsForJavaLangClassTest.java
@@ -0,0 +1,141 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.assumenosideeffects;
+
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithName;
+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 com.android.tools.r8.AssumeMayHaveSideEffects;
+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.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+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 AssumeNoSideEffectsForJavaLangClassTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return TestBase.getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public AssumeNoSideEffectsForJavaLangClassTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .enableSideEffectAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithEmptyOutput();
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject testClassSubject = inspector.clazz(TestClass.class);
+ assertThat(testClassSubject, isPresent());
+
+ inspectMethod(testClassSubject.uniqueMethodWithName("testModelingOfSideEffects"), false, false);
+ inspectMethod(
+ testClassSubject.uniqueMethodWithName("testModelingOfSideEffectsMaybeNull"), true, false);
+ inspectMethod(
+ testClassSubject.uniqueMethodWithName("testModelingOfSideEffectsMaybeSubclass"),
+ false,
+ true);
+ }
+
+ private void inspectMethod(
+ MethodSubject methodSubject, boolean maybeNullReceiver, boolean maybeSubtype) {
+ assertThat(methodSubject, isPresent());
+ assertThat(
+ methodSubject, onlyIf(maybeNullReceiver || maybeSubtype, invokesMethodWithName("equals")));
+ assertThat(
+ methodSubject,
+ onlyIf(maybeNullReceiver || maybeSubtype, invokesMethodWithName("hashCode")));
+ assertThat(methodSubject, onlyIf(maybeNullReceiver, invokesMethodWithName("getClass")));
+ assertThat(
+ methodSubject,
+ onlyIf(maybeNullReceiver || maybeSubtype, invokesMethodWithName("toString")));
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ testModelingOfSideEffects();
+ testModelingOfSideEffectsMaybeNull();
+ testModelingOfSideEffectsMaybeSubclass();
+ }
+
+ @AssumeMayHaveSideEffects
+ @NeverInline
+ static void testModelingOfSideEffects() {
+ Object o = new Object();
+ o.equals(new Object());
+ o.hashCode();
+ o.getClass();
+ o.toString();
+ }
+
+ @NeverInline
+ static void testModelingOfSideEffectsMaybeNull() {
+ createNullableObject().equals(new Object());
+ createNullableObject().hashCode();
+ createNullableObject().getClass();
+ createNullableObject().toString();
+ }
+
+ @NeverInline
+ static void testModelingOfSideEffectsMaybeSubclass() {
+ Object o = System.currentTimeMillis() > 0 ? new Object() : new A();
+ o.equals(new Object());
+ o.hashCode();
+ o.getClass();
+ o.toString();
+ }
+
+ static Object createNullableObject() {
+ return System.currentTimeMillis() > 0 ? new Object() : null;
+ }
+ }
+
+ static class A {
+
+ public A() {
+ super();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ throw new RuntimeException();
+ }
+
+ @Override
+ public int hashCode() {
+ throw new RuntimeException();
+ }
+
+ @Override
+ public String toString() {
+ throw new RuntimeException();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsForLibraryMethodTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsForLibraryMethodTest.java
index 632863a..9d74c64 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsForLibraryMethodTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsForLibraryMethodTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.ArrayList;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -36,7 +37,8 @@
testForR8(parameters.getBackend())
.addProgramClasses(TestClass.class)
.addKeepMainRule(TestClass.class)
- .addKeepRules("-assumenosideeffects class java.lang.Object { int hashCode() return 42; }")
+ .addKeepRules(
+ "-assumenosideeffects class java.util.ArrayList { int hashCode() return 42; }")
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(
@@ -58,7 +60,7 @@
static class TestClass {
public static void main(String[] args) {
- System.out.println(new Object().hashCode());
+ System.out.println(new ArrayList<>().hashCode());
}
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationWithSuperCallTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationWithSuperCallTest.java
index ecf26e9..640fd85 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationWithSuperCallTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationWithSuperCallTest.java
@@ -15,13 +15,14 @@
@RunWith(Parameterized.class)
public class AssumenosideeffectsPropagationWithSuperCallTest extends TestBase {
private static final Class<?> MAIN = DelegatesUser.class;
- private static final String JVM_OUTPUT = StringUtils.lines(
- "[Base] message1",
- "The end"
- );
- private static final String OUTPUT_WITHOUT_MESSAGES = StringUtils.lines(
- "The end"
- );
+ private static final String EXPECTED_OUTPUT =
+ StringUtils.lines("[Base] message1", "[Base] message2", "The end");
+
+ // With horizontal class merging enabled the method body for debug is cleared, because the
+ // forwarded call to the class specific implementation has no side effects. The call to the
+ // function from main persists.
+ private static final String EXPECTED_OUTPUT_WITH_HORIZONTAL_CLASS_MERGING =
+ StringUtils.lines("[Base] message2", "The end");
enum TestConfig {
SPECIFIC_RULES,
@@ -46,11 +47,13 @@
}
}
- public String expectedOutput() {
+ public String expectedOutput(TestParameters parameters) {
switch (this) {
case SPECIFIC_RULES:
case NON_SPECIFIC_RULES_WITH_EXTENDS:
- return JVM_OUTPUT;
+ return parameters.isCfRuntime()
+ ? EXPECTED_OUTPUT
+ : EXPECTED_OUTPUT_WITH_HORIZONTAL_CLASS_MERGING;
default:
throw new Unreachable();
}
@@ -62,7 +65,8 @@
@Parameterized.Parameters(name = "{0} {1}")
public static Collection<Object[]> data() {
- return buildParameters(getTestParameters().withAllRuntimes().build(), TestConfig.values());
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), TestConfig.values());
}
public AssumenosideeffectsPropagationWithSuperCallTest(
@@ -73,15 +77,14 @@
@Test
public void testR8() throws Exception {
- expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(AssumenosideeffectsPropagationWithSuperCallTest.class)
.addKeepMainRule(MAIN)
.addKeepRules(config.getKeepRules())
.noMinification()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
- .assertSuccessWithOutput(config.expectedOutput());
+ .assertSuccessWithOutput(config.expectedOutput(parameters));
}
static class BaseClass {
@@ -113,6 +116,10 @@
public static void main(String... args) {
BaseClass instance = createBase();
instance.debug("message1");
+ if (System.currentTimeMillis() > 0) {
+ instance = new BaseClass();
+ }
+ instance.debug("message2");
System.out.println("The end");
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/B152492625.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/B152492625.java
index 5b0f344..8da32bc 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/B152492625.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/B152492625.java
@@ -7,17 +7,19 @@
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticOrigin;
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticPosition;
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
+import static java.util.Collections.emptyList;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.ProguardVersion;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
@@ -25,14 +27,12 @@
import com.android.tools.r8.position.TextPosition;
import com.android.tools.r8.position.TextRange;
import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import java.util.List;
import org.hamcrest.Matcher;
-import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -54,53 +54,45 @@
this.dontWarnObject = dontWarnObject;
}
- private void noCallToWait(CodeInspector inspector) {
- ClassSubject classSubject = inspector.clazz(TestClass.class);
- assertThat(classSubject, isPresent());
- classSubject.forAllMethods(
- foundMethodSubject ->
- foundMethodSubject
- .instructions(InstructionSubject::isInvokeVirtual)
- .forEach(
- instructionSubject -> {
- Assert.assertNotEquals(
- "wait", instructionSubject.getMethod().name.toString());
- }));
+ private void checkIfWaitIsInvokedFromMain(CodeInspector inspector, boolean isR8) {
+ MethodSubject mainMethodSubject = inspector.clazz(TestClass.class).mainMethod();
+ assertThat(mainMethodSubject, isPresent());
+ assertThat(
+ mainMethodSubject,
+ onlyIf(isR8, invokesMethod("void", "java.lang.Object", "wait", emptyList())));
}
private Matcher<Diagnostic> matchAssumeNoSideEffectsMessage() {
return diagnosticMessage(
containsString(
- "The -assumenosideeffects rule matches methods on `java.lang.Object` with"
- + " wildcards"));
+ "The -assumenosideeffects rule matches the following method(s) on java.lang.Object: "));
}
private Matcher<Diagnostic> matchMessageForAllProblematicMethods() {
return diagnosticMessage(
allOf(
- containsString("void notify()"),
- containsString("void notifyAll()"),
- containsString("void wait()"),
- containsString("void wait(long)"),
- containsString("void wait(long, int)")));
+ containsString("notify()"),
+ containsString("notifyAll()"),
+ containsString("wait()"),
+ containsString("wait(long)"),
+ containsString("wait(long, int)")));
}
private Matcher<Diagnostic> matchMessageForWaitMethods() {
return diagnosticMessage(
allOf(
- containsString("void wait()"),
- containsString("void wait(long)"),
- containsString("void wait(long, int)")));
+ containsString("wait()"),
+ containsString("wait(long)"),
+ containsString("wait(long, int)")));
}
private void assertErrorsOrWarnings(
TestDiagnosticMessages diagnostics, List<Matcher<Diagnostic>> matchers) {
if (dontWarnObject) {
+ diagnostics.assertNoMessages();
+ } else {
diagnostics.assertOnlyWarnings();
diagnostics.assertWarningsMatch(matchers);
- } else {
- diagnostics.assertOnlyErrors();
- diagnostics.assertErrorsMatch(matchers);
}
}
@@ -115,23 +107,18 @@
ImmutableList.of(
allOf(matchAssumeNoSideEffectsMessage(), matchMessageForAllProblematicMethods()));
- try {
- testForR8(parameters.getBackend())
- .addProgramClasses(TestClass.class, B.class)
- .addKeepMainRule(TestClass.class)
- .applyIf(dontWarnObject, tb -> tb.addKeepRules("-dontwarn java.lang.Object"))
- .addKeepRules("-assumenosideeffects class " + B.class.getTypeName() + " { *; }")
- .setMinApi(parameters.getApiLevel())
- .allowDiagnosticWarningMessages()
- .compileWithExpectedDiagnostics(
- diagnostics -> assertErrorsOrWarnings(diagnostics, matchers))
- .inspect(this::noCallToWait)
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutputLines("Hello, world");
- assertTrue(dontWarnObject);
- } catch (CompilationFailedException e) {
- assertFalse(dontWarnObject);
- }
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class, B.class)
+ .addKeepMainRule(TestClass.class)
+ .applyIf(dontWarnObject, tb -> tb.addKeepRules("-dontwarn java.lang.Object"))
+ .addKeepRules("-assumenosideeffects class " + B.class.getTypeName() + " { *; }")
+ .setMinApi(parameters.getApiLevel())
+ .allowDiagnosticWarningMessages(!dontWarnObject)
+ .compileWithExpectedDiagnostics(
+ diagnostics -> assertErrorsOrWarnings(diagnostics, matchers))
+ .inspect(inspector -> checkIfWaitIsInvokedFromMain(inspector, true))
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello, world");
}
@Test
@@ -169,30 +156,24 @@
diagnosticOrigin(methodsRuleOrigin),
diagnosticPosition(textRangeForString(methodsRule))));
- try {
- testForR8(parameters.getBackend())
- .addProgramClasses(TestClass.class, B.class)
- .addKeepMainRule(TestClass.class)
- .applyIf(dontWarnObject, tb -> tb.addKeepRules("-dontwarn java.lang.Object"))
- .apply(
- b ->
- b.getBuilder()
- .addProguardConfiguration(ImmutableList.of(starRule), starRuleOrigin))
- .apply(
- b ->
- b.getBuilder()
- .addProguardConfiguration(ImmutableList.of(methodsRule), methodsRuleOrigin))
- .setMinApi(parameters.getApiLevel())
- .allowDiagnosticWarningMessages()
- .compileWithExpectedDiagnostics(
- diagnostics -> assertErrorsOrWarnings(diagnostics, matchers))
- .inspect(this::noCallToWait)
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutputLines("Hello, world");
- assertTrue(dontWarnObject);
- } catch (CompilationFailedException e) {
- assertFalse(dontWarnObject);
- }
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class, B.class)
+ .addKeepMainRule(TestClass.class)
+ .applyIf(dontWarnObject, tb -> tb.addKeepRules("-dontwarn java.lang.Object"))
+ .apply(
+ b ->
+ b.getBuilder().addProguardConfiguration(ImmutableList.of(starRule), starRuleOrigin))
+ .apply(
+ b ->
+ b.getBuilder()
+ .addProguardConfiguration(ImmutableList.of(methodsRule), methodsRuleOrigin))
+ .setMinApi(parameters.getApiLevel())
+ .allowDiagnosticWarningMessages(!dontWarnObject)
+ .compileWithExpectedDiagnostics(
+ diagnostics -> assertErrorsOrWarnings(diagnostics, matchers))
+ .inspect(inspector -> checkIfWaitIsInvokedFromMain(inspector, true))
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello, world");
}
@Test
@@ -203,22 +184,9 @@
.applyIf(dontWarnObject, tb -> tb.addKeepRules("-dontwarn java.lang.Object"))
.addKeepRules("-assumenosideeffects class " + B.class.getTypeName() + " { hash*(); }")
.setMinApi(parameters.getApiLevel())
- .allowDiagnosticWarningMessages(!dontWarnObject)
- .compileWithExpectedDiagnostics(
- diagnostics -> {
- if (dontWarnObject) {
- diagnostics.assertNoMessages();
- } else {
- diagnostics.assertOnlyWarnings();
- diagnostics.assertWarningsMatch(
- allOf(
- matchAssumeNoSideEffectsMessage(),
- diagnosticMessage(containsString("int hashCode()"))));
- }
- })
+ .compile()
.run(parameters.getRuntime(), TestClass.class)
- // Code fails with exception if wait call is not removed.
- .assertFailureWithErrorThatThrows(IllegalMonitorStateException.class);
+ .assertSuccessWithOutputLines("Hello, world");
}
@Test
@@ -227,23 +195,18 @@
ImmutableList.of(
allOf(matchAssumeNoSideEffectsMessage(), matchMessageForAllProblematicMethods()));
- try {
- testForR8(parameters.getBackend())
- .addProgramClasses(TestClass.class, B.class)
- .addKeepMainRule(TestClass.class)
- .applyIf(dontWarnObject, tb -> tb.addKeepRules("-dontwarn java.lang.Object"))
- .addKeepRules("-assumenosideeffects class " + B.class.getTypeName() + " { <methods>; }")
- .setMinApi(parameters.getApiLevel())
- .allowDiagnosticWarningMessages()
- .compileWithExpectedDiagnostics(
- diagnostics -> assertErrorsOrWarnings(diagnostics, matchers))
- .inspect(this::noCallToWait)
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutputLines("Hello, world");
- assertTrue(dontWarnObject);
- } catch (CompilationFailedException e) {
- assertFalse(dontWarnObject);
- }
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class, B.class)
+ .addKeepMainRule(TestClass.class)
+ .applyIf(dontWarnObject, tb -> tb.addKeepRules("-dontwarn java.lang.Object"))
+ .addKeepRules("-assumenosideeffects class " + B.class.getTypeName() + " { <methods>; }")
+ .setMinApi(parameters.getApiLevel())
+ .allowDiagnosticWarningMessages(!dontWarnObject)
+ .compileWithExpectedDiagnostics(
+ diagnostics -> assertErrorsOrWarnings(diagnostics, matchers))
+ .inspect(inspector -> checkIfWaitIsInvokedFromMain(inspector, true))
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello, world");
}
@Test
@@ -251,36 +214,35 @@
List<Matcher<Diagnostic>> matchers =
ImmutableList.of(allOf(matchAssumeNoSideEffectsMessage(), matchMessageForWaitMethods()));
- try {
- testForR8(parameters.getBackend())
- .addProgramClasses(TestClass.class, B.class)
- .addKeepMainRule(TestClass.class)
- .applyIf(dontWarnObject, tb -> tb.addKeepRules("-dontwarn java.lang.Object"))
- .addKeepRules("-assumenosideeffects class " + B.class.getTypeName() + " { *** w*(...); }")
- .setMinApi(parameters.getApiLevel())
- .allowDiagnosticWarningMessages()
- .compileWithExpectedDiagnostics(
- diagnostics -> assertErrorsOrWarnings(diagnostics, matchers))
- .inspect(this::noCallToWait)
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutputLines("Hello, world");
- assertTrue(dontWarnObject);
- } catch (CompilationFailedException e) {
- assertFalse(dontWarnObject);
- }
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class, B.class)
+ .addKeepMainRule(TestClass.class)
+ .applyIf(dontWarnObject, tb -> tb.addKeepRules("-dontwarn java.lang.Object"))
+ .addKeepRules("-assumenosideeffects class " + B.class.getTypeName() + " { *** w*(...); }")
+ .setMinApi(parameters.getApiLevel())
+ .allowDiagnosticWarningMessages(!dontWarnObject)
+ .compileWithExpectedDiagnostics(
+ diagnostics -> assertErrorsOrWarnings(diagnostics, matchers))
+ .inspect(inspector -> checkIfWaitIsInvokedFromMain(inspector, true))
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello, world");
}
@Test
public void testR8WaitSpecificMethodMatch() throws Exception {
assumeTrue("No need to run this with -dontwarn java.lang.Object", !dontWarnObject);
+ List<Matcher<Diagnostic>> matchers = ImmutableList.of(matchAssumeNoSideEffectsMessage());
+
testForR8(parameters.getBackend())
.addProgramClasses(TestClass.class, B.class)
.addKeepMainRule(TestClass.class)
.addKeepRules("-assumenosideeffects class java.lang.Object { void wait(); }")
.setMinApi(parameters.getApiLevel())
- .compile()
- .inspect(this::noCallToWait)
+ .allowDiagnosticWarningMessages()
+ .compileWithExpectedDiagnostics(
+ diagnostics -> assertErrorsOrWarnings(diagnostics, matchers))
+ .inspect(inspector -> checkIfWaitIsInvokedFromMain(inspector, true))
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutputLines("Hello, world");
}
@@ -313,7 +275,7 @@
assumeTrue("No need to run this with -dontwarn java.lang.Object", !dontWarnObject);
assumeTrue(parameters.isCfRuntime());
- testForProguard()
+ testForProguard(ProguardVersion.getLatest())
.addProgramClasses(TestClass.class, B.class)
.addKeepMainRule(TestClass.class)
.addKeepRules("-assumenosideeffects class " + B.class.getTypeName() + " { *; }")
@@ -321,7 +283,7 @@
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), TestClass.class)
- .assertFailureWithErrorThatThrows(IllegalMonitorStateException.class);
+ .assertSuccessWithOutputLines("Hello, world");
}
@Test
@@ -329,24 +291,28 @@
assumeTrue("No need to run this with -dontwarn java.lang.Object", !dontWarnObject);
assumeTrue(parameters.isCfRuntime());
- testForProguard()
+ testForProguard(ProguardVersion.getLatest())
.addProgramClasses(TestClass.class, B.class)
.addKeepMainRule(TestClass.class)
.addKeepRules("-assumenosideeffects class java.lang.Object { void wait(); }")
.addKeepRules("-dontwarn " + B152492625.class.getTypeName())
.setMinApi(parameters.getApiLevel())
.compile()
- .inspect(this::noCallToWait)
+ .inspect(inspector -> checkIfWaitIsInvokedFromMain(inspector, false))
.run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutputLines("Hello, world");
+ .assertSuccessWithOutput("Hello");
}
static class TestClass {
public void m() throws Exception {
- System.out.println("Hello, world");
- // test fails if wait is not removed.
- wait();
+ System.out.print("Hello");
+ // test fails if wait is removed.
+ try {
+ wait();
+ } catch (IllegalMonitorStateException e) {
+ System.out.println(", world");
+ }
}
public static void main(String[] args) throws Exception {
diff --git a/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumeValuesForLibraryMethodTest.java b/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumeValuesForLibraryMethodTest.java
index 4d7e293..852409f 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumeValuesForLibraryMethodTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumevalues/AssumeValuesForLibraryMethodTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.ArrayList;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -36,7 +37,7 @@
testForR8(parameters.getBackend())
.addProgramClasses(TestClass.class)
.addKeepMainRule(TestClass.class)
- .addKeepRules("-assumevalues class java.lang.Object { int hashCode() return 42; }")
+ .addKeepRules("-assumevalues class java.util.ArrayList { int hashCode() return 42; }")
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(
@@ -58,7 +59,7 @@
static class TestClass {
public static void main(String[] args) {
- System.out.println(new Object().hashCode());
+ System.out.println(new ArrayList<>().hashCode());
}
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/attributes/ClassSignaturesTest.java b/src/test/java/com/android/tools/r8/shaking/attributes/ClassSignaturesTest.java
deleted file mode 100644
index 7ca532d..0000000
--- a/src/test/java/com/android/tools/r8/shaking/attributes/ClassSignaturesTest.java
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.shaking.attributes;
-
-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.NeverClassInline;
-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.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import java.util.Iterator;
-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 ClassSignaturesTest extends TestBase {
-
- private final TestParameters parameters;
- private final String EXPECTED = "Hello World!";
-
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
- }
-
- public ClassSignaturesTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
- @Test
- public void testRuntime() throws Exception {
- testForRuntime(parameters)
- .addProgramClasses(A.class, Main.class)
- .run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(EXPECTED)
- .inspect(this::inspect);
- }
-
- @Test
- public void testR8() throws Exception {
- testForR8(parameters.getBackend())
- .addProgramClasses(A.class, Main.class)
- .addKeepMainRule(Main.class)
- .setMinApi(parameters.getApiLevel())
- .enableNeverClassInliningAnnotations()
- .enableInliningAnnotations()
- .addKeepAttributes(
- ProguardKeepAttributes.SIGNATURE,
- ProguardKeepAttributes.INNER_CLASSES,
- ProguardKeepAttributes.ENCLOSING_METHOD)
- .run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(EXPECTED)
- .inspect(this::inspect);
- }
-
- private void inspect(CodeInspector inspector) {
- ClassSubject aClass = inspector.clazz(A.class);
- assertThat(aClass, isPresent());
- assertEquals(
- "Ljava/lang/Object;Ljava/lang/Iterable<"
- + "Lcom/android/tools/r8/shaking/attributes/ClassSignaturesTest$Main;>;",
- aClass.getFinalSignatureAttribute());
- }
-
- @NeverClassInline
- public static class A implements Iterable<Main> {
-
- @Override
- @NeverInline
- public Iterator<Main> iterator() {
- throw new RuntimeException("FooBar");
- }
- }
-
- public static class Main {
-
- public static void main(String[] args) {
- try {
- new A().iterator();
- } catch (RuntimeException e) {
- if (!e.getMessage().contains("FooBar")) {
- throw e;
- }
- System.out.println("Hello World!");
- }
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/shaking/attributes/KeepSignatureTest.java b/src/test/java/com/android/tools/r8/shaking/attributes/KeepSignatureTest.java
index b441e6b..05f9731 100644
--- a/src/test/java/com/android/tools/r8/shaking/attributes/KeepSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/attributes/KeepSignatureTest.java
@@ -56,8 +56,7 @@
@Test
public void testKeptClassFieldAndMethodFull() throws Exception {
- // TODO(b/170077516): The below should pass in false.
- runTest(testForR8(parameters.getBackend()), true);
+ runTest(testForR8(parameters.getBackend()), false);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
index a3ad6d1..accb38b 100644
--- a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
@@ -7,16 +7,31 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
-import java.nio.file.Path;
-import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+@RunWith(Parameterized.class)
public class IncludeDescriptorClassesTest extends TestBase {
+ private final TestParameters testParameters;
+
+ public IncludeDescriptorClassesTest(TestParameters parameters) {
+ this.testParameters = parameters;
+ }
+
+ @Parameterized.Parameters(name = "{0}, horizontalClassMerging:{1}")
+ public static List<Object[]> data() {
+ return buildParameters(getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
private class Result {
final CodeInspector inspector;
final CodeInspector proguardedInspector;
@@ -26,7 +41,7 @@
this.proguardedInspector = proguardedInspector;
}
- void assertKept(Class clazz) {
+ void assertKept(Class<?> clazz) {
assertTrue(inspector.clazz(clazz.getCanonicalName()).isPresent());
assertFalse(inspector.clazz(clazz.getCanonicalName()).isRenamed());
if (proguardedInspector != null) {
@@ -35,14 +50,14 @@
}
// NOTE: 'synchronized' is supposed to disable inlining of this method.
- synchronized void assertRemoved(Class clazz) {
+ synchronized void assertRemoved(Class<?> clazz) {
assertFalse(inspector.clazz(clazz.getCanonicalName()).isPresent());
if (proguardedInspector != null) {
assertFalse(proguardedInspector.clazz(clazz).isPresent());
}
}
- void assertRenamed(Class clazz) {
+ void assertRenamed(Class<?> clazz) {
assertTrue(inspector.clazz(clazz.getCanonicalName()).isPresent());
assertTrue(inspector.clazz(clazz.getCanonicalName()).isRenamed());
if (proguardedInspector != null) {
@@ -59,22 +74,36 @@
NativeReturnType.class,
StaticFieldType.class,
InstanceFieldType.class);
- private List<Class> mainClasses = ImmutableList.of(
- MainCallMethod1.class, MainCallMethod2.class, MainCallMethod3.class);
+ private List<Class<?>> mainClasses =
+ ImmutableList.of(MainCallMethod1.class, MainCallMethod2.class, MainCallMethod3.class);
- Result runTest(Class mainClass, Path proguardConfig) throws Exception {
- List<Class<?>> classes = new ArrayList<>(applicationClasses);
- classes.add(mainClass);
+ Result runTest(ThrowableConsumer<TestShrinkerBuilder<?, ?, ?, ?, ?>> configure) throws Exception {
+ return runTest(configure, ignore -> {});
+ }
- CodeInspector inspector = new CodeInspector(compileWithR8(classes, proguardConfig));
+ Result runTest(
+ ThrowableConsumer<TestShrinkerBuilder<?, ?, ?, ?, ?>> configure,
+ ThrowableConsumer<R8FullTestBuilder> configureR8)
+ throws Exception {
+ CodeInspector inspector =
+ testForR8(testParameters.getBackend())
+ .setMinApi(testParameters.getApiLevel())
+ .addProgramClasses(applicationClasses)
+ .apply(configure::accept)
+ .apply(configureR8)
+ .compile()
+ .inspector();
CodeInspector proguardedInspector = null;
// Actually running Proguard should only be during development.
if (isRunProguard()) {
- Path proguardedJar = temp.newFolder().toPath().resolve("proguarded.jar");
- Path proguardedMap = temp.newFolder().toPath().resolve("proguarded.map");
- ToolHelper.runProguard(jarTestClasses(classes), proguardedJar, proguardConfig, proguardedMap);
- proguardedInspector = new CodeInspector(readJar(proguardedJar), proguardedMap);
+ proguardedInspector =
+ testForProguard()
+ .setMinApi(testParameters.getApiLevel())
+ .addProgramClasses(applicationClasses)
+ .apply(configure::accept)
+ .compile()
+ .inspector();
}
return new Result(inspector, proguardedInspector);
@@ -83,20 +112,21 @@
@Test
public void testNoIncludesDescriptorClasses() throws Exception {
for (Class<?> mainClass : mainClasses) {
- List<Class<?>> allClasses = new ArrayList<>(applicationClasses);
- allClasses.add(mainClass);
+ List<String> proguardConfig =
+ ImmutableList.of(
+ "-keepclasseswithmembers class * { ",
+ " <fields>; ",
+ " native <methods>; ",
+ "} ",
+ "-allowaccessmodification ");
- Path proguardConfig = writeTextToTempFile(
- keepMainProguardConfiguration(mainClass),
- "-keepclasseswithmembers class * { ",
- " <fields>; ",
- " native <methods>; ",
- "} ",
- "-allowaccessmodification ",
- "-printmapping "
- );
-
- Result result = runTest(mainClass, proguardConfig);
+ Result result =
+ runTest(
+ builder ->
+ builder
+ .addProgramClasses(mainClass)
+ .addKeepMainRule(mainClass)
+ .addKeepRules(proguardConfig));
result.assertKept(ClassWithNativeMethods.class);
// Return types are not removed as they can be needed for verification.
@@ -113,17 +143,20 @@
@Test
public void testKeepClassesWithMembers() throws Exception {
for (Class mainClass : mainClasses) {
- Path proguardConfig = writeTextToTempFile(
- keepMainProguardConfiguration(mainClass),
- "-keepclasseswithmembers,includedescriptorclasses class * { ",
- " <fields>; ",
- " native <methods>; ",
- "} ",
- "-allowaccessmodification ",
- "-printmapping "
- );
-
- Result result = runTest(mainClass, proguardConfig);
+ List<String> proguardConfig =
+ ImmutableList.of(
+ "-keepclasseswithmembers,includedescriptorclasses class * { ",
+ " <fields>; ",
+ " native <methods>; ",
+ "} ",
+ "-allowaccessmodification ");
+ Result result =
+ runTest(
+ builder ->
+ builder
+ .addProgramClasses(mainClass)
+ .addKeepMainRule(mainClass)
+ .addKeepRules(proguardConfig));
// With includedescriptorclasses return type, argument type ad field type are not renamed.
result.assertKept(ClassWithNativeMethods.class);
@@ -137,17 +170,21 @@
@Test
public void testKeepClassMembers() throws Exception {
for (Class mainClass : mainClasses) {
- Path proguardConfig = writeTextToTempFile(
- keepMainProguardConfiguration(mainClass),
- "-keepclassmembers,includedescriptorclasses class * { ",
- " <fields>; ",
- " native <methods>; ",
- "} ",
- "-allowaccessmodification ",
- "-printmapping "
- );
+ List<String> proguardConfig =
+ ImmutableList.of(
+ "-keepclassmembers,includedescriptorclasses class * { ",
+ " <fields>; ",
+ " native <methods>; ",
+ "} ",
+ "-allowaccessmodification ");
- Result result = runTest(mainClass, proguardConfig);
+ Result result =
+ runTest(
+ builder ->
+ builder
+ .addProgramClasses(mainClass)
+ .addKeepMainRule(mainClass)
+ .addKeepRules(proguardConfig));
// With includedescriptorclasses return type and argument type are not renamed.
result.assertRenamed(ClassWithNativeMethods.class);
@@ -160,20 +197,23 @@
@Test
public void testKeepClassMemberNames() throws Exception {
- expectThrowsWithHorizontalClassMerging();
for (Class<?> mainClass : mainClasses) {
- Path proguardConfig = writeTextToTempFile(
- keepMainProguardConfiguration(mainClass),
- // same as -keepclassmembers,allowshrinking,includedescriptorclasses
- "-keepclassmembernames,includedescriptorclasses class * { ",
- " <fields>; ",
- " native <methods>; ",
- "} ",
- "-allowaccessmodification ",
- "-printmapping "
- );
+ List<String> proguardConfig =
+ ImmutableList.of(
+ // same as -keepclassmembers,allowshrinking,includedescriptorclasses
+ "-keepclassmembernames,includedescriptorclasses class * { ",
+ " <fields>; ",
+ " native <methods>; ",
+ "} ",
+ "-allowaccessmodification ");
- Result result = runTest(mainClass, proguardConfig);
+ Result result =
+ runTest(
+ builder ->
+ builder
+ .addProgramClasses(mainClass)
+ .addKeepMainRule(mainClass)
+ .addKeepRules(proguardConfig));
boolean useNativeArgumentType =
mainClass == MainCallMethod1.class || mainClass == MainCallMethod3.class;
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/NativeReturnType.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/NativeReturnType.java
index f68a434..5290df7 100644
--- a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/NativeReturnType.java
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/NativeReturnType.java
@@ -4,6 +4,4 @@
package com.android.tools.r8.shaking.includedescriptorclasses;
-public class NativeReturnType {
-
-}
+public class NativeReturnType {}
diff --git a/src/test/java/com/android/tools/r8/shaking/librarymethodoverride/LibraryMethodOverrideTest.java b/src/test/java/com/android/tools/r8/shaking/librarymethodoverride/LibraryMethodOverrideTest.java
index 908f9f7..104f2cd 100644
--- a/src/test/java/com/android/tools/r8/shaking/librarymethodoverride/LibraryMethodOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/librarymethodoverride/LibraryMethodOverrideTest.java
@@ -9,6 +9,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -37,13 +38,13 @@
@Test
public void test() throws Exception {
- expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(LibraryMethodOverrideTest.class)
.addKeepMainRule(TestClass.class)
.addOptionsModification(options -> options.enableTreeShakingOfLibraryMethodOverrides = true)
.enableNeverClassInliningAnnotations()
.enableNoVerticalClassMergingAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::verifyOutput)
@@ -125,6 +126,7 @@
}
@NeverClassInline
+ @NoHorizontalClassMerging
static class DoesNotEscape {
DoesNotEscape() {
@@ -139,6 +141,7 @@
}
@NeverClassInline
+ @NoHorizontalClassMerging
static class DoesNotEscapeWithSubThatDoesNotOverride {
DoesNotEscapeWithSubThatDoesNotOverride() {
@@ -163,6 +166,7 @@
}
@NeverClassInline
+ @NoHorizontalClassMerging
static class DoesNotEscapeWithSubThatOverrides {
DoesNotEscapeWithSubThatOverrides() {
@@ -195,6 +199,7 @@
// EscapeWithSubThatOverridesAndEscapesSub is escaping from main(), unlike DoesNotEscapeWithSub-
// ThatOverridesSub.
@NeverClassInline
+ @NoHorizontalClassMerging
static class DoesNotEscapeWithSubThatOverridesAndEscapes {
DoesNotEscapeWithSubThatOverridesAndEscapes() {
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
index 578376e..e85f2d8 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.code.InvokeStaticRange;
import com.android.tools.r8.code.InvokeVirtual;
import com.android.tools.r8.code.MoveResult;
+import com.android.tools.r8.code.MoveResultObject;
import com.android.tools.r8.code.MoveResultWide;
import com.android.tools.r8.code.Return;
import com.android.tools.r8.code.ReturnObject;
@@ -42,7 +43,6 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
-import java.util.IdentityHashMap;
import java.util.List;
import java.util.function.Consumer;
import org.junit.Assert;
@@ -1299,24 +1299,23 @@
SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
// The result from neither the div-int is never used.
- MethodSignature signature = builder.addStaticMethod(
- "void",
- DEFAULT_METHOD_NAME,
- ImmutableList.of("int", "int"),
- 1,
- " div-int v0, p0, p1",
- " new-instance v0, Ljava/lang/StringBuilder;",
- " invoke-direct { v0 }, Ljava/lang/StringBuilder;-><init>()V",
- " return-void"
- );
+ MethodSignature signature =
+ builder.addStaticMethod(
+ "java.lang.Object",
+ DEFAULT_METHOD_NAME,
+ ImmutableList.of("int", "int"),
+ 1,
+ " div-int v0, p0, p1",
+ " new-instance v0, Ljava/lang/StringBuilder;",
+ " invoke-direct { v0 }, Ljava/lang/StringBuilder;-><init>()V",
+ " return-object v0");
builder.addMainMethod(
2,
" const v0, 1",
" const v1, 2",
- " invoke-static { v0, v1 }, LTest;->method(II)V",
- " return-void"
- );
+ " invoke-static { v0, v1 }, LTest;->method(II)Ljava/lang/Object;",
+ " return-void");
Consumer<InternalOptions> options =
configureOptions(
@@ -1324,20 +1323,6 @@
opts.outline.threshold = 1;
opts.outline.minSize = 3;
opts.outline.maxSize = 3;
-
- // Do not allow dead code elimination of the new-instance and invoke-direct
- // instructions.
- // This can be achieved by not assuming that StringBuilder is present.
- DexItemFactory dexItemFactory = opts.itemFactory;
- dexItemFactory.libraryTypesAssumedToBePresent =
- new HashSet<>(dexItemFactory.libraryTypesAssumedToBePresent);
- dexItemFactory.libraryTypesAssumedToBePresent.remove(
- dexItemFactory.stringBuilderType);
- // ... and not assuming that StringBuilder.<init>() cannot have side effects.
- dexItemFactory.libraryMethodsWithoutSideEffects =
- new IdentityHashMap<>(dexItemFactory.libraryMethodsWithoutSideEffects);
- dexItemFactory.libraryMethodsWithoutSideEffects.remove(
- dexItemFactory.stringBuilderMethods.defaultConstructor);
});
AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
@@ -1347,9 +1332,10 @@
// Return the processed method for inspection.
DexEncodedMethod method = getMethod(processedApplication, signature);
DexCode code = method.getCode().asDexCode();
- assertEquals(2, code.instructions.length);
+ assertEquals(3, code.instructions.length);
assertTrue(code.instructions[0] instanceof InvokeStatic);
- assertTrue(code.instructions[1] instanceof ReturnVoid);
+ assertTrue(code.instructions[1] instanceof MoveResultObject);
+ assertTrue(code.instructions[2] instanceof ReturnObject);
InvokeStatic invoke = (InvokeStatic) code.instructions[0];
assertEquals(firstOutlineMethodName(), invoke.getMethod().qualifiedName());
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java
index 86a21c1..ed4d158 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java
@@ -6,7 +6,9 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
+import java.util.List;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
@@ -71,10 +73,93 @@
};
}
+ public static Matcher<MethodSubject> invokesMethod(
+ String returnType, String holderType, String methodName, List<String> parameterTypes) {
+ return new TypeSafeMatcher<MethodSubject>() {
+ @Override
+ protected boolean matchesSafely(MethodSubject subject) {
+ if (!subject.isPresent()) {
+ return false;
+ }
+ if (!subject.getMethod().hasCode()) {
+ return false;
+ }
+ return subject
+ .streamInstructions()
+ .anyMatch(isInvokeWithTarget(returnType, holderType, methodName, parameterTypes));
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ StringBuilder text =
+ new StringBuilder("invokes method `")
+ .append(returnType != null ? returnType : "*")
+ .append(" ")
+ .append(holderType != null ? holderType : "*")
+ .append(".")
+ .append(methodName != null ? methodName : "*")
+ .append("(");
+ if (parameterTypes != null) {
+ text.append(
+ parameterTypes.stream()
+ .map(parameterType -> parameterType != null ? parameterType : "*")
+ .collect(Collectors.joining(", ")));
+ } else {
+ text.append("...");
+ }
+ text.append(")`");
+ description.appendText(text.toString());
+ }
+
+ @Override
+ public void describeMismatchSafely(final MethodSubject subject, Description description) {
+ description.appendText("method did not");
+ }
+ };
+ }
+
+ public static Matcher<MethodSubject> invokesMethodWithName(String name) {
+ return invokesMethod(null, null, name, null);
+ }
+
public static Predicate<InstructionSubject> isInvokeWithTarget(DexMethod target) {
return instruction -> instruction.isInvoke() && instruction.getMethod() == target;
}
+ public static Predicate<InstructionSubject> isInvokeWithTarget(
+ String returnType, String holderType, String methodName, List<String> parameterTypes) {
+ return instruction -> {
+ if (!instruction.isInvoke()) {
+ return false;
+ }
+ DexMethod invokedMethod = instruction.getMethod();
+ if (returnType != null
+ && !invokedMethod.getReturnType().toSourceString().equals(returnType)) {
+ return false;
+ }
+ if (holderType != null
+ && !invokedMethod.getHolderType().toSourceString().equals(holderType)) {
+ return false;
+ }
+ if (methodName != null && !invokedMethod.getName().toSourceString().equals(methodName)) {
+ return false;
+ }
+ if (parameterTypes != null) {
+ if (parameterTypes.size() != invokedMethod.getArity()) {
+ return false;
+ }
+ for (int i = 0; i < parameterTypes.size(); i++) {
+ String parameterType = parameterTypes.get(i);
+ if (parameterType != null
+ && !invokedMethod.getParameter(i).toSourceString().equals(parameterType)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ };
+ }
+
public static Predicate<InstructionSubject> isFieldAccessWithTarget(DexField target) {
return instruction -> instruction.isFieldAccess() && instruction.getField() == target;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
index 98c8562..29bebe1 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
@@ -713,6 +713,10 @@
}
}
+ public static <T> Matcher<T> onlyIf(boolean condition, Matcher<T> matcher) {
+ return notIf(matcher, !condition);
+ }
+
public static <T> Matcher<T> notIf(Matcher<T> matcher, boolean condition) {
if (condition) {
return not(matcher);
diff --git a/tools/r8_release.py b/tools/r8_release.py
index ce56269..cc4bebd 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -15,7 +15,7 @@
import utils
-R8_DEV_BRANCH = '2.3'
+R8_DEV_BRANCH = '3.0'
R8_VERSION_FILE = os.path.join(
'src', 'main', 'java', 'com', 'android', 'tools', 'r8', 'Version.java')
THIS_FILE_RELATIVE = os.path.join('tools', 'r8_release.py')
diff --git a/tools/test.py b/tools/test.py
index 64d2ae0..d7987a7 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -142,9 +142,6 @@
result.add_option('--worktree',
default=False, action='store_true',
help='Tests are run in worktree and should not use gradle user home.')
- result.add_option('--horizontal-class-merging', '--horizontal_class_merging',
- help='Enable horizontal class merging.',
- default=False, action='store_true')
result.add_option('--runtimes',
default=None,
help='Test parameter runtimes to use, separated by : (eg, none:jdk9).'
@@ -297,9 +294,6 @@
return_code = gradle.RunGradle(gradle_args, throw_on_failure=False)
return archive_and_return(return_code, options)
- if options.horizontal_class_merging:
- gradle_args.append('-PhorizontalClassMerging')
-
# Now run tests on selected runtime(s).
if options.runtimes:
if options.dex_vm != 'default':