Merge commit 'f0e2cd9064daa176dd9a6a3bf831c0e8310475f2' into dev-release
Change-Id: I4def325a506af616debaacd7574eb3c48d21631b
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index 360bb74..92666b6 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -7,8 +7,38 @@
"common_flags": [
{
"api_level_below_or_equal": 10000,
+ "amend_library_field": [
+ "public static java.time.LocalDate java.time.LocalDate#EPOCH"
+ ],
"amend_library_method": [
- "public java.lang.Object[] java.util.Collection#toArray(java.util.function.IntFunction)"
+ "public int java.time.Duration#toHoursPart()",
+ "public int java.time.Duration#toMillisPart()",
+ "public int java.time.Duration#toMinutesPart()",
+ "public int java.time.Duration#toNanosPart()",
+ "public int java.time.Duration#toSecondsPart()",
+ "public java.lang.Object[] java.util.Collection#toArray(java.util.function.IntFunction)",
+ "public java.time.Duration java.time.Duration#truncatedTo(java.time.temporal.TemporalUnit)",
+ "public java.time.temporal.ChronoUnit java.util.concurrent.TimeUnit#toChronoUnit()",
+ "public java.util.List java.util.stream.Stream#toList()",
+ "public java.util.stream.Stream java.time.LocalDate#datesUntil(java.time.LocalDate)",
+ "public java.util.stream.Stream java.time.LocalDate#datesUntil(java.time.LocalDate, java.time.Period)",
+ "public long java.time.LocalDate#toEpochSecond(java.time.LocalTime, java.time.ZoneOffset)",
+ "public long java.time.OffsetTime#toEpochSecond(java.time.LocalDate)",
+ "public long java.time.Duration#dividedBy(java.time.Duration)",
+ "public long java.time.Duration#toDaysPart()",
+ "public long java.time.Duration#toSeconds()",
+ "public long java.time.LocalTime#toEpochSecond(java.time.LocalDate, java.time.ZoneOffset)",
+ "public long java.util.concurrent.TimeUnit#convert(java.time.Duration)",
+ "public static java.time.Clock java.time.Clock#tickMillis(java.time.ZoneId)",
+ "public static java.time.LocalDate java.time.LocalDate#ofInstant(java.time.Instant, java.time.ZoneId)",
+ "public static java.time.LocalTime java.time.LocalTime#ofInstant(java.time.Instant, java.time.ZoneId)",
+ "public static java.util.concurrent.TimeUnit java.util.concurrent.TimeUnit#of(java.time.temporal.ChronoUnit)",
+ "public static java.util.stream.Collector java.util.stream.Collectors#filtering(java.util.function.Predicate, java.util.stream.Collector)",
+ "public static java.util.stream.Collector java.util.stream.Collectors#flatMapping(java.util.function.Function, java.util.stream.Collector)",
+ "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableList()",
+ "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function)",
+ "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function, java.util.function.BinaryOperator)",
+ "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableSet()"
]
},
{
@@ -29,18 +59,7 @@
"long java.time.LocalDate#toEpochSecond(java.time.LocalTime, java.time.ZoneOffset)": "java.time.DesugarLocalDate",
"java.time.Clock java.time.Clock#tickMillis(java.time.ZoneId)": "java.time.DesugarClock",
"long java.time.OffsetTime#toEpochSecond(java.time.LocalDate)": "java.time.DesugarOffsetTime"
- },
- "amend_library_field": [
- "public static java.time.LocalDate java.time.LocalDate#EPOCH"
- ],
- "amend_library_method": [
- "public java.util.stream.Stream java.time.LocalDate#datesUntil(java.time.LocalDate)",
- "public java.util.stream.Stream java.time.LocalDate#datesUntil(java.time.LocalDate, java.time.Period)",
- "public static java.time.LocalDate java.time.LocalDate#ofInstant(java.time.Instant, java.time.ZoneId)",
- "public long java.time.LocalDate#toEpochSecond(java.time.LocalTime, java.time.ZoneOffset)",
- "public static java.time.Clock java.time.Clock#tickMillis(java.time.ZoneId)",
- "public long java.time.OffsetTime#toEpochSecond(java.time.LocalDate)"
- ]
+ }
},
{
"api_level_below_or_equal": 10000,
@@ -67,12 +86,7 @@
"java.util.concurrent.TimeUnit java.util.concurrent.TimeUnit#of(java.time.temporal.ChronoUnit)": "java.util.concurrent.DesugarTimeUnit",
"java.time.temporal.ChronoUnit java.util.concurrent.TimeUnit#toChronoUnit()": "java.util.concurrent.DesugarTimeUnit",
"long java.util.concurrent.TimeUnit#convert(java.time.Duration)": "java.util.concurrent.DesugarTimeUnit"
- },
- "amend_library_method": [
- "public static java.util.concurrent.TimeUnit java.util.concurrent.TimeUnit#of(java.time.temporal.ChronoUnit)",
- "public java.time.temporal.ChronoUnit java.util.concurrent.TimeUnit#toChronoUnit()",
- "public long java.util.concurrent.TimeUnit#convert(java.time.Duration)"
- ]
+ }
},
{
"api_level_below_or_equal": 29,
@@ -86,6 +100,18 @@
]
},
{
+ "api_level_below_or_equal": 33,
+ "api_level_greater_or_equal": 24,
+ "emulate_interface": {
+ "java.util.stream.Stream": {
+ "rewrittenType": "j$.util.stream.Stream",
+ "emulatedMethods": [
+ "public java.util.List java.util.stream.Stream#toList()"
+ ]
+ }
+ }
+ },
+ {
"api_level_below_or_equal": 32,
"api_level_greater_or_equal": 24,
"rewrite_prefix": {
@@ -106,15 +132,7 @@
"java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function)": "java.util.stream.DesugarCollectors",
"java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function, java.util.function.BinaryOperator)": "java.util.stream.DesugarCollectors",
"java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableSet()": "java.util.stream.DesugarCollectors"
- },
- "amend_library_method": [
- "public static java.util.stream.Collector java.util.stream.Collectors#filtering(java.util.function.Predicate, java.util.stream.Collector)",
- "public static java.util.stream.Collector java.util.stream.Collectors#flatMapping(java.util.function.Function, java.util.stream.Collector)",
- "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableList()",
- "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function)",
- "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function, java.util.function.BinaryOperator)",
- "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableSet()"
- ]
+ }
},
{
"api_level_below_or_equal": 30,
@@ -135,20 +153,7 @@
"java.time.Duration java.time.Duration#truncatedTo(java.time.temporal.TemporalUnit)": "java.time.DesugarDuration",
"java.time.LocalTime java.time.LocalTime#ofInstant(java.time.Instant, java.time.ZoneId)": "java.time.DesugarLocalTime",
"long java.time.LocalTime#toEpochSecond(java.time.LocalDate, java.time.ZoneOffset)": "java.time.DesugarLocalTime"
- },
- "amend_library_method": [
- "public long java.time.Duration#dividedBy(java.time.Duration)",
- "public long java.time.Duration#toDaysPart()",
- "public int java.time.Duration#toHoursPart()",
- "public int java.time.Duration#toMillisPart()",
- "public int java.time.Duration#toMinutesPart()",
- "public int java.time.Duration#toNanosPart()",
- "public long java.time.Duration#toSeconds()",
- "public int java.time.Duration#toSecondsPart()",
- "public java.time.Duration java.time.Duration#truncatedTo(java.time.temporal.TemporalUnit)",
- "public static java.time.LocalTime java.time.LocalTime#ofInstant(java.time.Instant, java.time.ZoneId)",
- "public long java.time.LocalTime#toEpochSecond(java.time.LocalDate, java.time.ZoneOffset)"
- ]
+ }
},
{
"api_level_below_or_equal": 25,
@@ -368,6 +373,7 @@
{
"api_level_below_or_equal": 10000,
"amend_library_method": [
+ "public java.time.chrono.IsoEra java.time.LocalDate#getEra()",
"public static java.util.stream.DoubleStream java.util.stream.DoubleStream#iterate(double, java.util.function.DoublePredicate, java.util.function.DoubleUnaryOperator)",
"public static java.util.stream.IntStream java.util.stream.IntStream#iterate(int, java.util.function.IntPredicate, java.util.function.IntUnaryOperator)",
"public static java.util.stream.LongStream java.util.stream.LongStream#iterate(long, java.util.function.LongPredicate, java.util.function.LongUnaryOperator)",
@@ -379,10 +385,7 @@
"api_level_greater_or_equal": 26,
"covariant_retarget_method": {
"java.time.chrono.IsoEra java.time.LocalDate#getEra()": "java.time.chrono.Era"
- },
- "amend_library_method": [
- "public java.time.chrono.IsoEra java.time.LocalDate#getEra()"
- ]
+ }
},
{
"api_level_below_or_equal": 25,
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
index b75ec43..7214afc 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
@@ -7,10 +7,46 @@
"common_flags": [
{
"api_level_below_or_equal": 10000,
+ "amend_library_field": [
+ "public static java.time.LocalDate java.time.LocalDate#EPOCH"
+ ],
"amend_library_method": [
+ "public int java.time.Duration#toHoursPart()",
+ "public int java.time.Duration#toMillisPart()",
+ "public int java.time.Duration#toMinutesPart()",
+ "public int java.time.Duration#toNanosPart()",
+ "public int java.time.Duration#toSecondsPart()",
"public java.lang.Object[] java.util.Collection#toArray(java.util.function.IntFunction)",
+ "public java.time.Duration java.time.Duration#truncatedTo(java.time.temporal.TemporalUnit)",
+ "public java.time.temporal.ChronoUnit java.util.concurrent.TimeUnit#toChronoUnit()",
+ "public java.util.List java.util.stream.Stream#toList()",
+ "public java.util.stream.Stream java.time.LocalDate#datesUntil(java.time.LocalDate)",
+ "public java.util.stream.Stream java.time.LocalDate#datesUntil(java.time.LocalDate, java.time.Period)",
+ "public long java.io.InputStream#transferTo(java.io.OutputStream)",
+ "public long java.io.ByteArrayInputStream#transferTo(java.io.OutputStream)",
+ "public long java.time.LocalDate#toEpochSecond(java.time.LocalTime, java.time.ZoneOffset)",
+ "public long java.time.OffsetTime#toEpochSecond(java.time.LocalDate)",
+ "public long java.time.Duration#dividedBy(java.time.Duration)",
+ "public long java.time.Duration#toDaysPart()",
+ "public long java.time.Duration#toSeconds()",
+ "public long java.time.LocalTime#toEpochSecond(java.time.LocalDate, java.time.ZoneOffset)",
+ "public long java.util.concurrent.TimeUnit#convert(java.time.Duration)",
+ "public static java.lang.String java.nio.file.Files#readString(java.nio.file.Path)",
+ "public static java.lang.String java.nio.file.Files#readString(java.nio.file.Path, java.nio.charset.Charset)",
+ "public static java.nio.file.Path java.nio.file.Files#writeString(java.nio.file.Path, java.lang.CharSequence, java.nio.file.OpenOption[])",
+ "public static java.nio.file.Path java.nio.file.Files#writeString(java.nio.file.Path, java.lang.CharSequence, java.nio.charset.Charset, java.nio.file.OpenOption[])",
"public static java.nio.file.Path java.nio.file.Path#of(java.lang.String, java.lang.String[])",
- "public static java.nio.file.Path java.nio.file.Path#of(java.net.URI)"
+ "public static java.nio.file.Path java.nio.file.Path#of(java.net.URI)",
+ "public static java.time.Clock java.time.Clock#tickMillis(java.time.ZoneId)",
+ "public static java.time.LocalDate java.time.LocalDate#ofInstant(java.time.Instant, java.time.ZoneId)",
+ "public static java.time.LocalTime java.time.LocalTime#ofInstant(java.time.Instant, java.time.ZoneId)",
+ "public static java.util.concurrent.TimeUnit java.util.concurrent.TimeUnit#of(java.time.temporal.ChronoUnit)",
+ "public static java.util.stream.Collector java.util.stream.Collectors#filtering(java.util.function.Predicate, java.util.stream.Collector)",
+ "public static java.util.stream.Collector java.util.stream.Collectors#flatMapping(java.util.function.Function, java.util.stream.Collector)",
+ "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableList()",
+ "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function)",
+ "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function, java.util.function.BinaryOperator)",
+ "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableSet()"
]
},
{
@@ -36,22 +72,7 @@
"java.lang.String java.nio.file.Files#readString(java.nio.file.Path, java.nio.charset.Charset)": "java.nio.file.DesugarFiles",
"java.nio.file.Path java.nio.file.Files#writeString(java.nio.file.Path, java.lang.CharSequence, java.nio.file.OpenOption[])": "java.nio.file.DesugarFiles",
"java.nio.file.Path java.nio.file.Files#writeString(java.nio.file.Path, java.lang.CharSequence, java.nio.charset.Charset, java.nio.file.OpenOption[])": "java.nio.file.DesugarFiles"
- },
- "amend_library_field": [
- "public static java.time.LocalDate java.time.LocalDate#EPOCH"
- ],
- "amend_library_method": [
- "public java.util.stream.Stream java.time.LocalDate#datesUntil(java.time.LocalDate)",
- "public java.util.stream.Stream java.time.LocalDate#datesUntil(java.time.LocalDate, java.time.Period)",
- "public static java.time.LocalDate java.time.LocalDate#ofInstant(java.time.Instant, java.time.ZoneId)",
- "public long java.time.LocalDate#toEpochSecond(java.time.LocalTime, java.time.ZoneOffset)",
- "public static java.time.Clock java.time.Clock#tickMillis(java.time.ZoneId)",
- "public long java.time.OffsetTime#toEpochSecond(java.time.LocalDate)",
- "public static java.lang.String java.nio.file.Files#readString(java.nio.file.Path)",
- "public static java.lang.String java.nio.file.Files#readString(java.nio.file.Path, java.nio.charset.Charset)",
- "public static java.nio.file.Path java.nio.file.Files#writeString(java.nio.file.Path, java.lang.CharSequence, java.nio.file.OpenOption[])",
- "public static java.nio.file.Path java.nio.file.Files#writeString(java.nio.file.Path, java.lang.CharSequence, java.nio.charset.Charset, java.nio.file.OpenOption[])"
- ]
+ }
},
{
"api_level_below_or_equal": 10000,
@@ -85,14 +106,7 @@
"retarget_method_with_emulated_dispatch": {
"long java.io.InputStream#transferTo(java.io.OutputStream)": "java.io.DesugarInputStream",
"long java.io.ByteArrayInputStream#transferTo(java.io.OutputStream)": "long java.io.DesugarInputStream#transferTo(java.io.InputStream, java.io.OutputStream)"
- },
- "amend_library_method": [
- "public static java.util.concurrent.TimeUnit java.util.concurrent.TimeUnit#of(java.time.temporal.ChronoUnit)",
- "public java.time.temporal.ChronoUnit java.util.concurrent.TimeUnit#toChronoUnit()",
- "public long java.util.concurrent.TimeUnit#convert(java.time.Duration)",
- "public long java.io.InputStream#transferTo(java.io.OutputStream)",
- "public long java.io.ByteArrayInputStream#transferTo(java.io.OutputStream)"
- ]
+ }
},
{
"api_level_below_or_equal": 29,
@@ -106,6 +120,18 @@
]
},
{
+ "api_level_below_or_equal": 33,
+ "api_level_greater_or_equal": 24,
+ "emulate_interface": {
+ "java.util.stream.Stream": {
+ "rewrittenType": "j$.util.stream.Stream",
+ "emulatedMethods": [
+ "public java.util.List java.util.stream.Stream#toList()"
+ ]
+ }
+ }
+ },
+ {
"api_level_below_or_equal": 32,
"api_level_greater_or_equal": 24,
"rewrite_prefix": {
@@ -126,15 +152,7 @@
"java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function)": "java.util.stream.DesugarCollectors",
"java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function, java.util.function.BinaryOperator)": "java.util.stream.DesugarCollectors",
"java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableSet()": "java.util.stream.DesugarCollectors"
- },
- "amend_library_method": [
- "public static java.util.stream.Collector java.util.stream.Collectors#filtering(java.util.function.Predicate, java.util.stream.Collector)",
- "public static java.util.stream.Collector java.util.stream.Collectors#flatMapping(java.util.function.Function, java.util.stream.Collector)",
- "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableList()",
- "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function)",
- "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableMap(java.util.function.Function, java.util.function.Function, java.util.function.BinaryOperator)",
- "public static java.util.stream.Collector java.util.stream.Collectors#toUnmodifiableSet()"
- ]
+ }
},
{
"api_level_below_or_equal": 30,
@@ -155,20 +173,7 @@
"java.time.Duration java.time.Duration#truncatedTo(java.time.temporal.TemporalUnit)": "java.time.DesugarDuration",
"java.time.LocalTime java.time.LocalTime#ofInstant(java.time.Instant, java.time.ZoneId)": "java.time.DesugarLocalTime",
"long java.time.LocalTime#toEpochSecond(java.time.LocalDate, java.time.ZoneOffset)": "java.time.DesugarLocalTime"
- },
- "amend_library_method": [
- "public long java.time.Duration#dividedBy(java.time.Duration)",
- "public long java.time.Duration#toDaysPart()",
- "public int java.time.Duration#toHoursPart()",
- "public int java.time.Duration#toMillisPart()",
- "public int java.time.Duration#toMinutesPart()",
- "public int java.time.Duration#toNanosPart()",
- "public long java.time.Duration#toSeconds()",
- "public int java.time.Duration#toSecondsPart()",
- "public java.time.Duration java.time.Duration#truncatedTo(java.time.temporal.TemporalUnit)",
- "public static java.time.LocalTime java.time.LocalTime#ofInstant(java.time.Instant, java.time.ZoneId)",
- "public long java.time.LocalTime#toEpochSecond(java.time.LocalDate, java.time.ZoneOffset)"
- ]
+ }
},
{
"api_level_below_or_equal": 25,
@@ -527,6 +532,7 @@
{
"api_level_below_or_equal": 10000,
"amend_library_method": [
+ "public java.time.chrono.IsoEra java.time.LocalDate#getEra()",
"public static java.util.stream.DoubleStream java.util.stream.DoubleStream#iterate(double, java.util.function.DoublePredicate, java.util.function.DoubleUnaryOperator)",
"public static java.util.stream.IntStream java.util.stream.IntStream#iterate(int, java.util.function.IntPredicate, java.util.function.IntUnaryOperator)",
"public static java.util.stream.LongStream java.util.stream.LongStream#iterate(long, java.util.function.LongPredicate, java.util.function.LongUnaryOperator)",
@@ -546,10 +552,7 @@
"api_level_greater_or_equal": 26,
"covariant_retarget_method": {
"java.time.chrono.IsoEra java.time.LocalDate#getEra()": "java.time.chrono.Era"
- },
- "amend_library_method": [
- "public java.time.chrono.IsoEra java.time.LocalDate#getEra()"
- ]
+ }
},
{
"api_level_below_or_equal": 25,
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index ee368b2..087c305 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -524,7 +524,6 @@
assert ArtProfileCompletenessChecker.verify(appView);
LirConverter.rewriteLirWithLens(appView, timing, executorService);
- appView.clearCodeRewritings(executorService, timing);
VerticalClassMerger.createForInitialClassMerging(appViewWithLiveness, timing)
.runIfNecessary(executorService, timing);
@@ -757,13 +756,11 @@
}
GenericSignatureContextBuilder genericContextBuilderBeforeFinalMerging = null;
- if (appView.hasCfByteCodePassThroughMethods()) {
- LirConverter.rewriteLirWithLens(appView, timing, executorService);
- appView.clearCodeRewritings(executorService, timing);
- } else {
- // Rewrite LIR with lens to allow building IR from LIR in class mergers.
- LirConverter.rewriteLirWithLens(appView, timing, executorService);
- appView.clearCodeRewritings(executorService, timing);
+
+ // Rewrite LIR with lens to allow building IR from LIR in class mergers.
+ LirConverter.rewriteLirWithLens(appView, timing, executorService);
+
+ if (!appView.hasCfByteCodePassThroughMethods()) {
assert appView.dexItemFactory().verifyNoCachedTypeElements();
if (appView.hasLiveness()) {
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 612ecec1e..2d24e07 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -183,11 +183,10 @@
KeepInfoCollection keepInfo = appView.getKeepInfo();
keepInfo.mutate(mutator -> mutator.removeKeepInfoForMergedClasses(prunedItems));
appView.rewriteWithLens(horizontalClassMergerGraphLens, executorService, timing);
- LirConverter.rewriteLirWithLens(appViewWithClassHierarchy, timing, executorService);
new IdentifierMinifier(appViewWithClassHierarchy)
.rewriteDexItemBasedConstStringInStaticFields(executorService);
+ LirConverter.rewriteLirWithLens(appViewWithClassHierarchy, timing, executorService);
appView.rebuildAppInfo(newApplication);
- appView.clearCodeRewritings(executorService, timing);
} else {
SyntheticItems syntheticItems = appView.appInfo().getSyntheticItems();
assert !syntheticItems.hasPendingSyntheticClasses();
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
index 865d83b..5afa942 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
@@ -44,6 +44,10 @@
this.type = type;
}
+ public static Builder builder() {
+ return new Builder();
+ }
+
@Override
public int opcode() {
return Opcodes.ARRAY_GET;
@@ -299,4 +303,40 @@
public void buildLir(LirBuilder<Value, ?> builder) {
builder.addArrayGet(getMemberType(), array(), index());
}
+
+ public static class Builder extends BuilderBase<Builder, ArrayGet> {
+
+ private Value arrayValue;
+ private Value indexValue;
+ private MemberType memberType;
+
+ public Builder setArrayValue(Value arrayValue) {
+ this.arrayValue = arrayValue;
+ return this;
+ }
+
+ public Builder setIndexValue(Value indexValue) {
+ this.indexValue = indexValue;
+ return this;
+ }
+
+ public Builder setMemberType(MemberType memberType) {
+ this.memberType = memberType;
+ return this;
+ }
+
+ @Override
+ public ArrayGet build() {
+ if (memberType == null && outValue.getType().isReferenceType()) {
+ memberType = MemberType.OBJECT;
+ }
+ assert memberType != null;
+ return amend(new ArrayGet(memberType, outValue, arrayValue, indexValue));
+ }
+
+ @Override
+ public Builder self() {
+ return this;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 126f23a..9b4fffb 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -45,6 +45,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import java.util.function.Consumer;
import java.util.function.Predicate;
public abstract class Instruction
@@ -1764,6 +1765,13 @@
protected Value outValue;
protected Position position;
+ public final B applyIf(boolean condition, Consumer<B> consumer) {
+ if (condition) {
+ consumer.accept(self());
+ }
+ return self();
+ }
+
public abstract I build();
public abstract B self();
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 b60c42e..c448a2d 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
@@ -52,6 +52,11 @@
}
@Override
+ public Builder newBuilder() {
+ return builder();
+ }
+
+ @Override
public int opcode() {
return Opcodes.INVOKE_DIRECT;
}
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 f677699..e4c26ae 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
@@ -32,6 +32,15 @@
super(target, result, arguments);
}
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public Builder newBuilder() {
+ return builder();
+ }
+
@Override
public boolean getInterfaceBit() {
return true;
@@ -147,4 +156,17 @@
public void buildLir(LirBuilder<Value, ?> builder) {
builder.addInvokeInterface(getInvokedMethod(), arguments());
}
+
+ public static class Builder extends InvokeMethod.Builder<Builder, InvokeInterface> {
+
+ @Override
+ public InvokeInterface build() {
+ return amend(new InvokeInterface(method, outValue, arguments));
+ }
+
+ @Override
+ public Builder self() {
+ return this;
+ }
+ }
}
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 e15b67c..c40827c 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
@@ -77,6 +77,8 @@
}
}
+ public abstract Builder<? extends Builder, ? extends InvokeMethod> newBuilder();
+
public Value getFirstNonReceiverArgument() {
return getArgument(getFirstNonReceiverArgumentIndex());
}
@@ -346,11 +348,12 @@
return false;
}
- abstract static class Builder<B extends Builder<B, I>, I extends InvokeMethod>
+ public abstract static class Builder<B extends Builder<B, I>, I extends InvokeMethod>
extends BuilderBase<B, I> {
protected DexMethod method;
protected List<Value> arguments = Collections.emptyList();
+ protected boolean isInterface = false;
public B setArguments(Value... arguments) {
return setArguments(Arrays.asList(arguments));
@@ -367,6 +370,11 @@
factory, TypeElement.fromDexType(method.getReturnType(), maybeNull(), appView));
}
+ public B setIsInterface(boolean isInterface) {
+ this.isInterface = isInterface;
+ return self();
+ }
+
public B setSingleArgument(Value argument) {
return setArguments(ImmutableList.of(argument));
}
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 9ef005e..601448c 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
@@ -37,6 +37,11 @@
}
@Override
+ public Builder<? extends Builder, ? extends InvokeMethod> newBuilder() {
+ throw new Unreachable();
+ }
+
+ @Override
public boolean getInterfaceBit() {
return false;
}
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 8abaa2c..16f0c04 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,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
-
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.dex.code.DexInstruction;
import com.android.tools.r8.dex.code.DexInvokeStatic;
@@ -48,6 +47,11 @@
}
@Override
+ public Builder newBuilder() {
+ return builder();
+ }
+
+ @Override
public boolean getInterfaceBit() {
return isInterface;
}
@@ -229,7 +233,7 @@
assert method != null;
assert method.getArity() == arguments.size();
assert outValue == null || !method.getReturnType().isVoidType();
- return amend(new InvokeStatic(method, outValue, arguments));
+ return amend(new InvokeStatic(method, outValue, arguments, isInterface));
}
@Override
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 d1d9ad4..b5520a0 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
@@ -34,6 +34,15 @@
this.isInterface = isInterface;
}
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public Builder newBuilder() {
+ return builder();
+ }
+
@Override
public boolean getInterfaceBit() {
return isInterface;
@@ -132,4 +141,17 @@
public void buildLir(LirBuilder<Value, ?> builder) {
builder.addInvokeSuper(getInvokedMethod(), arguments(), isInterface);
}
+
+ public static class Builder extends InvokeMethod.Builder<Builder, InvokeSuper> {
+
+ @Override
+ public InvokeSuper build() {
+ return amend(new InvokeSuper(method, outValue, arguments, isInterface));
+ }
+
+ @Override
+ public Builder self() {
+ return this;
+ }
+ }
}
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 6d97ebe..b1cf3bd 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
@@ -37,6 +37,11 @@
}
@Override
+ public Builder newBuilder() {
+ return builder();
+ }
+
+ @Override
public boolean getInterfaceBit() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
index 60d1cd4..f17b896 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
@@ -35,6 +35,7 @@
import com.android.tools.r8.utils.ObjectUtils;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.ThrowingAction;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.verticalclassmerging.IncompleteVerticalClassMergerBridgeCode;
import java.util.concurrent.ExecutionException;
@@ -85,6 +86,19 @@
Timing timing,
ExecutorService executorService)
throws ExecutionException {
+ rewriteLirWithLens(
+ appView,
+ timing,
+ executorService,
+ () -> appView.clearCodeRewritings(executorService, timing));
+ }
+
+ public static <T extends Throwable> void rewriteLirWithLens(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ Timing timing,
+ ExecutorService executorService,
+ ThrowingAction<T> onChangedAction)
+ throws ExecutionException, T {
assert appView.testing().canUseLir(appView);
assert appView.testing().isSupportedLirPhase();
assert !appView.getSyntheticItems().hasPendingSyntheticClasses();
@@ -108,6 +122,8 @@
timing.begin("LIR->LIR@" + graphLens.getClass().getName());
rewriteLirWithUnappliedLens(appView, executorService);
timing.end();
+
+ onChangedAction.execute();
}
private static void rewriteLirWithUnappliedLens(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 04fb94e..7595d4e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -334,10 +334,7 @@
initializeAndroidQMethodProviders(factory);
}
if (options.getMinApiLevel().isLessThan(AndroidApiLevel.R)) {
- if (options.testing.alwaysBackportListSetMapMethods
- || typeIsPresentWithoutBackportsFrom(factory.javaUtilSetType, AndroidApiLevel.R)) {
- initializeAndroidRSetListMapMethodProviders(factory);
- }
+ initializeAndroidRSetListMapMethodProviders(factory);
if (typeIsAbsentOrPresentWithoutBackportsFrom(factory.objectsType, AndroidApiLevel.R)) {
initializeAndroidRObjectsMethodProviders(factory);
if (typeIsPresent(factory.supplierType)) {
@@ -347,10 +344,7 @@
}
if (options.getMinApiLevel().isLessThan(AndroidApiLevel.S)) {
initializeAndroidSMethodProviders(factory);
- if (options.testing.alwaysBackportListSetMapMethods
- || typeIsPresentWithoutBackportsFrom(factory.javaUtilSetType, AndroidApiLevel.S)) {
- initializeAndroidSSetListMapMethodProviders(factory);
- }
+ initializeAndroidSSetListMapMethodProviders(factory);
}
if (options.getMinApiLevel().isLessThan(AndroidApiLevel.Sv2)) {
initializeAndroidSv2MethodProviders(factory);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/ClassOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/ClassOptimizer.java
new file mode 100644
index 0000000..b166b9e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/ClassOptimizer.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.library;
+
+import com.android.tools.r8.graph.AppView;
+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;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlockIterator;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.optimize.AffectedValues;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.Set;
+
+public class ClassOptimizer extends StatelessLibraryMethodModelCollection {
+
+ private final InternalOptions options;
+ private final DexItemFactory dexItemFactory;
+ private final DexMethod getConstructor;
+ private final DexMethod getDeclaredConstructor;
+ private final DexMethod getMethod;
+ private final DexMethod getDeclaredMethod;
+
+ ClassOptimizer(AppView<?> appView) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ this.options = appView.options();
+ this.dexItemFactory = dexItemFactory;
+ getConstructor = dexItemFactory.classMethods.getConstructor;
+ getDeclaredConstructor = dexItemFactory.classMethods.getDeclaredConstructor;
+ getMethod = dexItemFactory.classMethods.getMethod;
+ getDeclaredMethod = dexItemFactory.classMethods.getDeclaredMethod;
+ }
+
+ @Override
+ public DexType getType() {
+ return dexItemFactory.classType;
+ }
+
+ @Override
+ public InstructionListIterator optimize(
+ IRCode code,
+ BasicBlockIterator blockIterator,
+ InstructionListIterator instructionIterator,
+ InvokeMethod invoke,
+ DexClassAndMethod singleTarget,
+ AffectedValues affectedValues,
+ Set<BasicBlock> blocksToRemove) {
+ DexMethod singleTargetReference = singleTarget.getReference();
+ if (singleTargetReference.isIdenticalTo(getConstructor)
+ || singleTargetReference.isIdenticalTo(getDeclaredConstructor)
+ || singleTargetReference.isIdenticalTo(getMethod)
+ || singleTargetReference.isIdenticalTo(getDeclaredMethod)) {
+ EmptyVarargsUtil.replaceWithNullIfEmptyArray(
+ invoke, invoke.arguments().size() - 1, code, instructionIterator, options);
+ assert instructionIterator.peekPrevious() == invoke;
+ }
+ return instructionIterator;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/ConstructorOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/ConstructorOptimizer.java
new file mode 100644
index 0000000..f0d66a9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/ConstructorOptimizer.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.library;
+
+import com.android.tools.r8.graph.AppView;
+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;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlockIterator;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.optimize.AffectedValues;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.Set;
+
+public class ConstructorOptimizer extends StatelessLibraryMethodModelCollection {
+
+ private final InternalOptions options;
+ private final DexItemFactory dexItemFactory;
+ private final DexMethod newInstance;
+
+ ConstructorOptimizer(AppView<?> appView) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ this.options = appView.options();
+ this.dexItemFactory = dexItemFactory;
+ newInstance = dexItemFactory.constructorMethods.newInstance;
+ }
+
+ @Override
+ public DexType getType() {
+ return dexItemFactory.constructorType;
+ }
+
+ @Override
+ public InstructionListIterator optimize(
+ IRCode code,
+ BasicBlockIterator blockIterator,
+ InstructionListIterator instructionIterator,
+ InvokeMethod invoke,
+ DexClassAndMethod singleTarget,
+ AffectedValues affectedValues,
+ Set<BasicBlock> blocksToRemove) {
+ DexMethod singleTargetReference = singleTarget.getReference();
+ if (singleTargetReference.isIdenticalTo(newInstance)) {
+ EmptyVarargsUtil.replaceWithNullIfEmptyArray(invoke, 1, code, instructionIterator, options);
+ assert instructionIterator.peekPrevious() == invoke;
+ }
+ return instructionIterator;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/EmptyVarargsUtil.java b/src/main/java/com/android/tools/r8/ir/optimize/library/EmptyVarargsUtil.java
new file mode 100644
index 0000000..61bc097
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/EmptyVarargsUtil.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.library;
+
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.InternalOptions;
+
+public class EmptyVarargsUtil {
+ public static void replaceWithNullIfEmptyArray(
+ InvokeMethod invoke,
+ int argumentIndex,
+ IRCode code,
+ InstructionListIterator instructionIterator,
+ InternalOptions options) {
+ Value argument = invoke.getArgument(argumentIndex);
+ if (argument.isDefinedByInstructionSatisfying(
+ i -> i.isNewArrayEmpty() && i.asNewArrayEmpty().sizeIfConst() == 0)) {
+ instructionIterator.previous();
+ Value replacement = instructionIterator.insertConstNullInstruction(code, options);
+ invoke.replaceValue(argumentIndex, replacement);
+ instructionIterator.next();
+ }
+ }
+}
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 cadab0f..788cbf6 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
@@ -46,9 +46,12 @@
this.appView = appView;
timing.begin("Register optimizers");
PrimitiveMethodOptimizer.forEachPrimitiveOptimizer(appView, this::register);
+ register(new ClassOptimizer(appView));
register(new CollectionsOptimizer(appView));
+ register(new ConstructorOptimizer(appView));
register(new ListOptimizer(appView));
register(new MapOptimizer(appView));
+ register(new MethodOptimizer(appView));
register(new ObjectMethodOptimizer(appView));
register(new ObjectsMethodOptimizer(appView));
register(new SetOptimizer(appView));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/MethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/MethodOptimizer.java
new file mode 100644
index 0000000..bf87ca7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/MethodOptimizer.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.library;
+
+import com.android.tools.r8.graph.AppView;
+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;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlockIterator;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.optimize.AffectedValues;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.Set;
+
+public class MethodOptimizer extends StatelessLibraryMethodModelCollection {
+
+ private final InternalOptions options;
+ private final DexItemFactory dexItemFactory;
+ private final DexMethod invoke;
+
+ MethodOptimizer(AppView<?> appView) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ this.options = appView.options();
+ this.dexItemFactory = dexItemFactory;
+ invoke = dexItemFactory.methodMethods.invoke;
+ }
+
+ @Override
+ public DexType getType() {
+ return dexItemFactory.methodType;
+ }
+
+ @Override
+ public InstructionListIterator optimize(
+ IRCode code,
+ BasicBlockIterator blockIterator,
+ InstructionListIterator instructionIterator,
+ InvokeMethod invoke,
+ DexClassAndMethod singleTarget,
+ AffectedValues affectedValues,
+ Set<BasicBlock> blocksToRemove) {
+ DexMethod singleTargetReference = singleTarget.getReference();
+ if (singleTargetReference.isIdenticalTo(this.invoke)) {
+ EmptyVarargsUtil.replaceWithNullIfEmptyArray(invoke, 2, code, instructionIterator, options);
+ assert instructionIterator.peekPrevious() == invoke;
+ }
+ return instructionIterator;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java
index bd2fdb3..3a14982 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java
@@ -330,6 +330,7 @@
throws ExecutionException {
// To simplify the analysis, we currently bail out for non-final classes.
// TODO(b/296030319): Handle non-final classes.
+ Set<DexType> serviceImplementations = appView.appServices().computeAllServiceImplementations();
MapUtils.removeIf(
nonFinalInstanceFields,
(holderType, fields) -> {
@@ -337,7 +338,8 @@
DexProgramClass holder = fields.iterator().next().getHolder();
// If the class is kept it could be instantiated directly, in which case all default field
// values could be live.
- if (appView.getKeepInfo(holder).isPinned(appView.options())) {
+ if (appView.getKeepInfo(holder).isPinned(appView.options())
+ || serviceImplementations.contains(holder.getType())) {
fields.forEach(liveDefaultValueConsumer);
return true;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
index 9d63f56..1b06eb4 100644
--- a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
@@ -158,7 +158,6 @@
updateEnclosingMethodAttributes(executorService, protoNormalizerGraphLens);
appView.rewriteWithLens(protoNormalizerGraphLens, executorService, timing);
LirConverter.rewriteLirWithLens(appView, timing, executorService);
- appView.clearCodeRewritings(executorService, timing);
}
appView.notifyOptimizationFinishedForTesting();
timing.end();
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 fbcab01..4a358ee 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -161,7 +161,6 @@
import com.android.tools.r8.utils.IteratorUtils;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.OptionalBool;
-import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
@@ -4131,10 +4130,6 @@
private final Map<DexProgramClass, Set<DexClass>> injectedInterfaces =
new ConcurrentHashMap<>();
- // Subset of live methods that need have keep requirements.
- private final List<Pair<ProgramMethod, Consumer<KeepMethodInfo.Joiner>>>
- liveMethodsWithKeepActions = new ArrayList<>();
-
SyntheticAdditions(ProcessorContext processorContext) {
this.processorContext = processorContext;
}
@@ -4150,7 +4145,6 @@
&& liveMethods.isEmpty()
&& syntheticClasspathClasses.isEmpty()
&& injectedInterfaces.isEmpty();
- assert !empty || liveMethodsWithKeepActions.isEmpty();
return empty;
}
@@ -4191,13 +4185,9 @@
// All synthetic additions are initial tree shaking only. No need to track keep reasons.
KeepReasonWitness fakeReason = enqueuer.graphReporter.fakeReportShouldNotBeUsed();
-
for (ProgramMethod desugaredMethod : desugaredMethods) {
enqueuer.worklist.enqueueTraceCodeAction(desugaredMethod);
}
-
- liveMethodsWithKeepActions.forEach(
- item -> enqueuer.keepInfo.joinMethod(item.getFirst(), item.getSecond()));
for (ProgramMethod liveMethod : liveMethods.values()) {
assert !enqueuer.targetedMethods.contains(liveMethod.getDefinition());
enqueuer.markMethodAsTargeted(liveMethod, fakeReason);
@@ -4209,7 +4199,6 @@
enqueuer.objectAllocationInfoCollection.injectInterfaces(
enqueuer.appInfo(), clazz, itfs);
});
-
minimumSyntheticKeepInfo.forEach(
(method, minimumKeepInfoForMethod) -> {
enqueuer.getKeepInfo().registerCompilerSynthesizedMethod(method);
diff --git a/src/main/java/com/android/tools/r8/synthesis/CallOtherMergeableSyntheticMethodUseRegistry.java b/src/main/java/com/android/tools/r8/synthesis/CallOtherMergeableSyntheticMethodUseRegistry.java
new file mode 100644
index 0000000..080fd8e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/synthesis/CallOtherMergeableSyntheticMethodUseRegistry.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2025, 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.synthesis;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DefaultUseRegistryWithResult;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.google.common.collect.Iterables;
+
+public class CallOtherMergeableSyntheticMethodUseRegistry
+ extends DefaultUseRegistryWithResult<Boolean, ProgramMethod> {
+
+ public CallOtherMergeableSyntheticMethodUseRegistry(AppView<?> appView, ProgramMethod context) {
+ super(appView, context, false);
+ }
+
+ @Override
+ public void registerInvokeStatic(DexMethod method) {
+ if (appView.getSyntheticItems().isSynthetic(method.getHolderType())
+ && isSyntheticMethodAllowingGlobalMerging(method)) {
+ setResult(true);
+ }
+ }
+
+ private boolean isSyntheticMethodAllowingGlobalMerging(DexMethod method) {
+ return Iterables.all(
+ appView.getSyntheticItems().getSyntheticKinds(method.getHolderType()),
+ kind ->
+ kind.isSyntheticMethodKind() && kind.asSyntheticMethodKind().isAllowGlobalMerging());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 5a83444..0b9f17c 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.fixup.TreeFixerBase;
import com.android.tools.r8.graph.lens.GraphLens;
@@ -586,7 +587,14 @@
potentialEquivalences.forEach(
members -> {
T t = members.get(0);
- if (t.getKind().mayCallOtherSyntheticMethods(appView.getSyntheticItems().getNaming())) {
+ boolean mayCallOtherSyntheticMethods = false;
+ if (t.isMethodDefinition()) {
+ ProgramMethod method = t.asMethodDefinition().getMethod();
+ mayCallOtherSyntheticMethods =
+ method.registerCodeReferencesWithResult(
+ new CallOtherMergeableSyntheticMethodUseRegistry(appView, method));
+ }
+ if (mayCallOtherSyntheticMethods) {
indirectPotentialEquivalences.add(members);
} else {
directPotentialEquivalences.add(members);
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index ebc0b1d..1ef8edc 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -259,11 +259,6 @@
public abstract boolean isSingleSyntheticMethod();
- @SuppressWarnings("ReferenceEquality")
- public boolean mayCallOtherSyntheticMethods(SyntheticNaming naming) {
- return this == naming.AUTOCLOSEABLE_DISPATCHER;
- }
-
public abstract boolean isFixedSuffixSynthetic();
public abstract boolean isGlobal();
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 f26afb8..8bf530b 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -2174,8 +2174,6 @@
return currentPhase == LirPhase.POST;
}
- // If false, use the desugared library implementation when desugared library is enabled.
- public boolean alwaysBackportListSetMapMethods = true;
public boolean neverReuseCfLocalRegisters = false;
public boolean checkReceiverAlwaysNullInCallSiteOptimization = true;
public boolean forceInlineAPIConversions = false;
diff --git a/src/main/java/com/android/tools/r8/utils/ThrowingAction.java b/src/main/java/com/android/tools/r8/utils/ThrowingAction.java
index d5b4fa8..a044eeb 100644
--- a/src/main/java/com/android/tools/r8/utils/ThrowingAction.java
+++ b/src/main/java/com/android/tools/r8/utils/ThrowingAction.java
@@ -5,6 +5,11 @@
package com.android.tools.r8.utils;
@FunctionalInterface
-public interface ThrowingAction<E extends Throwable> {
- void execute() throws E;
+public interface ThrowingAction<T extends Throwable> {
+
+ void execute() throws T;
+
+ static ThrowingAction<RuntimeException> empty() {
+ return () -> {};
+ }
}
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
index acb8b0d..86a3bba 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
@@ -29,6 +29,7 @@
import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.ThrowingAction;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.Timing.TimingMerger;
import com.google.common.collect.Streams;
@@ -250,7 +251,7 @@
private void rewriteCodeWithLens(ExecutorService executorService, Timing timing)
throws ExecutionException {
- LirConverter.rewriteLirWithLens(appView, timing, executorService);
+ LirConverter.rewriteLirWithLens(appView, timing, executorService, ThrowingAction.empty());
new IdentifierMinifier(appView).rewriteDexItemBasedConstStringInStaticFields(executorService);
}
diff --git a/src/test/examplesJava17/desugaredlibrary/StreamToListTest.java b/src/test/examplesJava17/desugaredlibrary/StreamToListTest.java
new file mode 100644
index 0000000..e9e4786
--- /dev/null
+++ b/src/test/examplesJava17/desugaredlibrary/StreamToListTest.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2025, 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 desugaredlibrary;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.SPECIFICATIONS_WITH_CF2CF;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.stream.Stream;
+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 StreamToListTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+ private final CompilationSpecification compilationSpecification;
+
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("a - c");
+
+ @Parameters(name = "{0}, spec: {1}, {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+ ImmutableList.of(JDK11, JDK11_PATH),
+ SPECIFICATIONS_WITH_CF2CF);
+ }
+
+ public StreamToListTest(
+ TestParameters parameters,
+ LibraryDesugaringSpecification libraryDesugaringSpecification,
+ CompilationSpecification compilationSpecification) {
+ this.parameters = parameters;
+ this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+ this.compilationSpecification = compilationSpecification;
+ }
+
+ @Test
+ public void test() throws Throwable {
+ testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addInnerClassesAndStrippedOuter(getClass())
+ .addKeepMainRule(Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ List<String> strings = Stream.of("a", "b", "c").toList();
+ System.out.println(strings.get(0) + " - " + strings.get(2));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/GetMajorAndMinorSdkVersionBackportOutlineInBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/GetMajorAndMinorSdkVersionBackportOutlineInBackportTest.java
index 4e39653..337c252 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/GetMajorAndMinorSdkVersionBackportOutlineInBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/GetMajorAndMinorSdkVersionBackportOutlineInBackportTest.java
@@ -137,10 +137,7 @@
assertEquals(
1,
countInvokeStaticToMethod(apiOutline1.uniqueMethod(), getGetMajorSdkVersion(inspector)));
- // TODO(b/382242341):There are 4 backports, as even though they are identical two by two, they
- // are not currently shared, as synthetic sharing does not see equivalence of the backports
- // calling the API outline synthetics, which are shared.
- for (int i = 2; i < 6; i++) {
+ for (int i = 2; i < 3; i++) {
ClassSubject backport =
inspector.clazz(
SyntheticItemsTestUtils.syntheticBackportWithForwardingClass(TestClass.class, i));
@@ -174,8 +171,6 @@
} else {
ClassSubject apiOutline0 =
inspector.clazz(SyntheticItemsTestUtils.syntheticApiOutlineClass(TestClass.class, 0));
- // TODO(b/382242341): There are 4 methods merged into the outline synthetic. Two calling
- // getMajorSdkVersion and two calling getMinorSdkVersion.
assertEquals(4, apiOutline0.allMethods().size());
MethodSubject main = inspector.clazz(TestClass.class).mainMethod();
assertEquals(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CollectionOfTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CollectionOfTest.java
index e58ce54..5792e22 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CollectionOfTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CollectionOfTest.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -55,34 +54,21 @@
this.compilationSpecification = compilationSpecification;
}
- private String getExpectedOutput(boolean desugaredLib, boolean alwaysBackportListSetMapMethods) {
+ private String getExpectedOutput() {
if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.R)) {
return EXPECTED_OUTPUT_CORRECT;
}
- if (alwaysBackportListSetMapMethods) {
- return EXPECTED_OUTPUT_BACKPORT;
- }
- if (desugaredLib && libraryDesugaringSpecification != JDK8) {
- if (parameters.getApiLevel().isLessThan(AndroidApiLevel.N)) {
- return EXPECTED_OUTPUT_CORRECT;
- }
- // TODO(b/243679691): This should also be correct, but is not because we use backports in
- // partial desugaring.
- return EXPECTED_OUTPUT_BACKPORT;
- }
return EXPECTED_OUTPUT_BACKPORT;
}
@Test
public void testCollectionOf() throws Throwable {
- for (Boolean value : BooleanUtils.values()) {
- testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
- .addProgramFiles(INPUT_JAR)
- .addKeepMainRule(MAIN_CLASS)
- .addOptionsModification(opt -> opt.testing.alwaysBackportListSetMapMethods = value)
- .run(parameters.getRuntime(), MAIN_CLASS)
- .assertSuccessWithOutput(getExpectedOutput(true, value));
- }
+ testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addProgramFiles(INPUT_JAR)
+ .addKeepMainRule(MAIN_CLASS)
+ .compile()
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(getExpectedOutput());
}
@Test
@@ -94,6 +80,6 @@
.addProgramFiles(INPUT_JAR)
.setMinApi(parameters)
.run(parameters.getRuntime(), MAIN_CLASS)
- .assertSuccessWithOutput(getExpectedOutput(false, true));
+ .assertSuccessWithOutput(getExpectedOutput());
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryInvokeAllResolveTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryInvokeAllResolveTest.java
index a6e298f..8893a42 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryInvokeAllResolveTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryInvokeAllResolveTest.java
@@ -68,8 +68,9 @@
+ " java.util.stream.LongStream.takeWhile(java.util.function.LongPredicate)",
"java.util.stream.DoubleStream"
+ " java.util.stream.DoubleStream.dropWhile(java.util.function.DoublePredicate)",
+ // Stream.toList() and FileStore.getBlockSize() were added in 33 which confuses the
+ // required library (30).
"java.util.List java.util.stream.Stream.toList()",
- // FileStore.getBlockSize() was added in 33 which confuses the required library (30).
"long java.nio.file.FileStore.getBlockSize()",
// The call is present but unreachable above 26.
"java.nio.channels.FileChannel"
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java
index 2c948b2..50cb8a9 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java
@@ -77,8 +77,7 @@
ImmutableSet.of(
"java.util.stream.Stream java.util.stream.Stream.takeWhile(java.util.function.Predicate)",
"java.util.stream.Stream"
- + " java.util.stream.Stream.dropWhile(java.util.function.Predicate)",
- "java.util.List java.util.stream.Stream.toList()");
+ + " java.util.stream.Stream.dropWhile(java.util.function.Predicate)");
private static final Set<String> FAILURES_NUMBER_STREAM =
ImmutableSet.of(
"java.util.stream.IntStream"
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
index 2faea77..ca43f85 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
@@ -94,14 +94,6 @@
parameters, libraryDesugaringSpecification, compilationSpecification)
.addProgramFiles(Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR + "stream.jar"))
.addKeepMainRule(TEST_CLASS)
- .applyIf(
- compilationSpecification.isProgramShrink(),
- b ->
- b.addOptionsModification(
- options -> {
- // TODO(b/140233505): Allow devirtualization once fixed.
- options.enableDevirtualization = false;
- }))
.compile()
.inspect(this::checkRewrittenInvokes)
.inspectKeepRules(
@@ -146,11 +138,25 @@
classSubject
.uniqueMethodWithOriginalName("main")
.streamInstructions()
- .filter(instr -> instr.isInvokeInterface() || instr.isInvokeStatic())
+ .filter(
+ instr ->
+ instr.isInvokeInterface() || instr.isInvokeStatic() || instr.isInvokeVirtual())
+ .filter(
+ instr -> {
+ String holder = instr.getMethod().getHolderType().getTypeName();
+ return !holder.equals("java.lang.Class")
+ && !holder.equals("java.lang.Object")
+ && !holder.equals("java.io.PrintStream");
+ })
.collect(Collectors.toList());
+ assertEquals(22, invokes.size());
assertInvokeStaticMatching(invokes, 0, "Set$-EL;spliterator");
assertInvokeStaticMatching(invokes, 1, "Collection$-EL;stream");
- assertInvokeInterfaceMatching(invokes, 2, "Set;iterator");
+ if (compilationSpecification.isProgramShrink()) {
+ assertInvokeVirtualMatching(invokes, 2, "HashSet;iterator");
+ } else {
+ assertInvokeInterfaceMatching(invokes, 2, "Set;iterator");
+ }
assertInvokeStaticMatching(invokes, 3, "Collection$-EL;stream");
assertInvokeStaticMatching(invokes, 4, "Set$-EL;spliterator");
assertInvokeInterfaceMatching(invokes, 8, "Iterator;remove");
@@ -165,7 +171,6 @@
assertInvokeStaticMatching(invokes, 19, "Comparator$-CC;comparingInt");
assertInvokeStaticMatching(invokes, 20, "List$-EL;sort");
assertInvokeStaticMatching(invokes, 21, "Collection$-EL;stream");
- assertEquals(22, invokes.size());
}
private void assertInvokeInterfaceMatching(List<InstructionSubject> invokes, int i, String s) {
@@ -178,6 +183,11 @@
assertTrue(invokes.get(i).toString().contains(s));
}
+ private void assertInvokeVirtualMatching(List<InstructionSubject> invokes, int i, String s) {
+ assertTrue(invokes.get(i).isInvokeVirtual());
+ assertTrue(invokes.get(i).toString().contains(s));
+ }
+
private void assertGeneratedKeepRulesAreCorrect(String keepRules) {
String prefix = libraryDesugaringSpecification.functionPrefix(parameters);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/library/EmptyVarargsDominanceTest.java b/src/test/java/com/android/tools/r8/ir/optimize/library/EmptyVarargsDominanceTest.java
new file mode 100644
index 0000000..24ba024
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/library/EmptyVarargsDominanceTest.java
@@ -0,0 +1,51 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.library;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.util.Arrays;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class EmptyVarargsDominanceTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("[]");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) throws NoSuchMethodException {
+ Class<?>[] emptyClassArray = new Class[0];
+ if (System.currentTimeMillis() == 0) {
+ System.out.println(Main.class.getConstructor(emptyClassArray));
+ } else {
+ System.out.println(Arrays.toString(emptyClassArray));
+ }
+ }
+
+ public Main() {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/library/EmptyVarargsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/library/EmptyVarargsTest.java
index ea72d77..2384387 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/library/EmptyVarargsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/library/EmptyVarargsTest.java
@@ -53,9 +53,9 @@
MethodSubject testMethod =
mainClassSubject.uniqueMethodWithOriginalName("testDeclared");
- assertTrue(testMethod.streamInstructions().anyMatch(InstructionSubject::isNewArray));
+ assertTrue(testMethod.streamInstructions().noneMatch(InstructionSubject::isNewArray));
testMethod = mainClassSubject.uniqueMethodWithOriginalName("testNonDeclared");
- assertTrue(testMethod.streamInstructions().anyMatch(InstructionSubject::isNewArray));
+ assertTrue(testMethod.streamInstructions().noneMatch(InstructionSubject::isNewArray));
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("hi", "hi");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/DefaultFieldValueJoinerWithServiceLoaderTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/DefaultFieldValueJoinerWithServiceLoaderTest.java
new file mode 100644
index 0000000..1efc4cb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/DefaultFieldValueJoinerWithServiceLoaderTest.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.membervaluepropagation;
+
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.Lists;
+import java.util.List;
+import java.util.ServiceLoader;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class DefaultFieldValueJoinerWithServiceLoaderTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ List<String> serviceImplementations = Lists.newArrayList();
+ serviceImplementations.add(B.class.getTypeName());
+ serviceImplementations.add(C.class.getTypeName());
+
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addDataResources(
+ DataEntryResource.fromBytes(
+ StringUtils.lines(serviceImplementations).getBytes(),
+ "META-INF/services/" + A.class.getTypeName(),
+ Origin.unknown()))
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("0", "1", "0", "2");
+ }
+
+ public interface A {
+
+ void init();
+
+ void print();
+ }
+
+ public static class B implements A {
+
+ int f;
+
+ @Override
+ public void init() {
+ f = 1;
+ }
+
+ @Override
+ public void print() {
+ System.out.println(f);
+ }
+ }
+
+ public static class C implements A {
+
+ int g;
+
+ @Override
+ public void init() {
+ g = 2;
+ }
+
+ @Override
+ public void print() {
+ System.out.println(g);
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ for (A a : ServiceLoader.load(A.class)) {
+ a.print();
+ a.init();
+ a.print();
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsOnMethodWithArgumentReturnTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsOnMethodWithArgumentReturnTest.java
new file mode 100644
index 0000000..9fb503f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumeNoSideEffectsOnMethodWithArgumentReturnTest.java
@@ -0,0 +1,107 @@
+// Copyright (c) 2025, 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.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+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.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.io.PrintStream;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AssumeNoSideEffectsOnMethodWithArgumentReturnTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepClassAndMembersRules(Main.class)
+ .addKeepRules(
+ "-assumenosideeffects class " + Preconditions.class.getTypeName() + " {",
+ " static java.lang.Object checkNotNull(java.lang.Object);",
+ "}")
+ .setMinApi(parameters)
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject preconditionsClass = inspector.clazz(Preconditions.class);
+ assertThat(preconditionsClass, isPresentIf(parameters.isCfRuntime()));
+
+ MethodSubject checkNotNullMethod =
+ preconditionsClass.uniqueMethodWithOriginalName("checkNotNull");
+ assertThat(checkNotNullMethod, isPresentIf(parameters.isCfRuntime()));
+
+ MethodSubject testMethodSubject =
+ inspector.clazz(Main.class).uniqueMethodWithOriginalName("test");
+ assertThat(testMethodSubject, isPresent());
+ if (parameters.isCfRuntime()) {
+ // Calls are not eliminated due to not running the move-result optimization when
+ // compiling to CF.
+ assertThat(testMethodSubject, invokesMethod(checkNotNullMethod));
+ } else {
+ assertTrue(
+ testMethodSubject
+ .streamInstructions()
+ .filter(InstructionSubject::isInvokeMethod)
+ .map(InstructionSubject::getMethod)
+ .allMatch(
+ method ->
+ method.toSourceString().contains(PrintStream.class.getTypeName())));
+ }
+ })
+ .run(parameters.getRuntime(), Main.class, "Hello", ",", " ", "world", "!")
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ test(args[0], args[1], args[2], args[3], args[4]);
+ }
+
+ static void test(Object p1, Object p2, Object p3, Object p4, Object p5) {
+ Object nonNullP1 = Preconditions.checkNotNull(p1);
+ System.out.print(nonNullP1);
+ Object nonNullP2 = Preconditions.checkNotNull(p2);
+ System.out.print(nonNullP2);
+ Object nonNullP3 = Preconditions.checkNotNull(p3);
+ System.out.print(nonNullP3);
+ Object nonNullP4 = Preconditions.checkNotNull(p4);
+ System.out.print(nonNullP4);
+ Object nonNullP5 = Preconditions.checkNotNull(p5);
+ System.out.println(nonNullP5);
+ }
+ }
+
+ static class Preconditions {
+
+ static <T> T checkNotNull(T t) {
+ if (t == null) {
+ throw new RuntimeException("Expected non-null object");
+ }
+ return t;
+ }
+ }
+}
diff --git a/src/test/testbase/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestCompileResult.java b/src/test/testbase/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestCompileResult.java
index 023803d..0f11fc1 100644
--- a/src/test/testbase/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestCompileResult.java
+++ b/src/test/testbase/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestCompileResult.java
@@ -61,6 +61,11 @@
this.runnableCompiledResult = computeRunnableCompiledResult();
}
+ public DesugaredLibraryTestCompileResult<T> disassemble() throws IOException, ExecutionException {
+ compileResult.disassemble();
+ return this;
+ }
+
public <E extends Throwable> DesugaredLibraryTestCompileResult<T> inspectCustomLib(
ThrowingConsumer<CodeInspector, E> consumer) throws IOException, E {
assert customLibCompile != null;