Merge commit '127105d72c0927207be3a56f436ab07a7fb26981' into dev-release
diff --git a/build.gradle b/build.gradle
index 8a6e218..532320f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -374,6 +374,7 @@
"openjdk/desugar_jdk_libs_releases/1.1.1",
"openjdk/desugar_jdk_libs_releases/1.1.5",
"openjdk/jdk-11-test",
+ "opensource-apps/tivi",
"proguard/proguard5.2.1",
"proguard/proguard6.0.1",
"proguard/proguard-7.0.0",
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index 503352b..2c892cd 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -13,16 +13,30 @@
}
},
{
+ "api_level_below_or_equal": 32,
+ "rewrite_prefix": {
+ "java.io.DesugarInputStream": "j$.io.DesugarInputStream"
+ },
+ "retarget_method_with_emulated_dispatch": {
+ "long java.io.InputStream#transferTo(java.io.OutputStream)": "java.io.DesugarInputStream"
+ },
+ "amend_library_method": [
+ "public long java.io.InputStream#transferTo(java.io.OutputStream)"
+ ]
+ },
+ {
"api_level_below_or_equal": 30,
"rewrite_prefix": {
"java.time.": "j$.time.",
"java.util.Desugar": "j$.util.Desugar"
},
"retarget_method": {
+ "java.util.TimeZone java.util.TimeZone#getTimeZone(java.time.ZoneId)": "java.util.DesugarTimeZone"
+ },
+ "retarget_method_with_emulated_dispatch": {
"java.time.Instant java.util.Date#toInstant()": "java.util.DesugarDate",
"java.time.ZoneId java.util.TimeZone#toZoneId()": "java.util.DesugarTimeZone",
- "java.time.ZonedDateTime java.util.GregorianCalendar#toZonedDateTime()": "java.util.DesugarGregorianCalendar",
- "java.util.TimeZone java.util.TimeZone#getTimeZone(java.time.ZoneId)": "java.util.DesugarTimeZone"
+ "java.time.ZonedDateTime java.util.GregorianCalendar#toZonedDateTime()": "java.util.DesugarGregorianCalendar"
},
"wrapper_conversion": [
"java.time.Clock"
@@ -70,7 +84,6 @@
"retarget_method": {
"java.util.Spliterator java.util.Arrays#spliterator(java.lang.Object[])": "java.util.DesugarArrays",
"java.util.Spliterator java.util.Arrays#spliterator(java.lang.Object[], int, int)": "java.util.DesugarArrays",
- "java.util.Spliterator java.util.LinkedHashSet#spliterator()": "java.util.DesugarLinkedHashSet",
"java.util.Spliterator$OfDouble java.util.Arrays#spliterator(double[])": "java.util.DesugarArrays",
"java.util.Spliterator$OfDouble java.util.Arrays#spliterator(double[], int, int)": "java.util.DesugarArrays",
"java.util.Spliterator$OfInt java.util.Arrays#spliterator(int[])": "java.util.DesugarArrays",
@@ -86,6 +99,9 @@
"java.util.stream.Stream java.util.Arrays#stream(java.lang.Object[])": "java.util.DesugarArrays",
"java.util.stream.Stream java.util.Arrays#stream(java.lang.Object[], int, int)": "java.util.DesugarArrays"
},
+ "retarget_method_with_emulated_dispatch": {
+ "java.util.Spliterator java.util.LinkedHashSet#spliterator()": "java.util.DesugarLinkedHashSet"
+ },
"wrapper_conversion": [
"java.util.function.IntUnaryOperator",
"java.util.function.BiFunction",
@@ -111,7 +127,6 @@
"java.util.stream.Stream",
"java.util.function.ObjLongConsumer",
"java.util.function.ToDoubleFunction",
- "java.util.Spliterator",
"java.util.stream.IntStream",
"java.util.function.LongBinaryOperator",
"java.util.Spliterator$OfDouble",
@@ -138,6 +153,12 @@
"java.util.stream.BaseStream",
"java.util.function.DoublePredicate"
],
+ "wrapper_conversion_excluding": {
+ "java.util.Spliterator": [
+ "boolean java.util.Spliterator#hasCharacteristics(int)",
+ "long java.util.Spliterator#getExactSizeIfKnown()"
+ ]
+ },
"custom_conversion": {
"java.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatisticsConversions",
"java.util.IntSummaryStatistics": "java.util.IntSummaryStatisticsConversions",
@@ -255,7 +276,9 @@
"sun.util.PreHashedMap": "j$.sun.util.PreHashedMap"
},
"retarget_method": {
- "boolean java.util.Arrays#deepEquals0(java.lang.Object, java.lang.Object)": "java.util.DesugarArrays",
+ "boolean java.util.Arrays#deepEquals0(java.lang.Object, java.lang.Object)": "java.util.DesugarArrays"
+ },
+ "retarget_method_with_emulated_dispatch": {
"java.nio.file.Path java.io.File#toPath()": "java.io.DesugarFile"
},
"backport": {
@@ -271,7 +294,6 @@
{
"api_level_below_or_equal": 23,
"rewrite_prefix": {
- "java.io.DesugarInputStream": "j$.io.DesugarInputStream",
"java.lang.FunctionalInterface": "j$.lang.FunctionalInterface",
"java.nio.channels.SeekableByteChannel": "j$.nio.channels.SeekableByteChannel",
"java.util.AbstractList": "j$.util.AbstractList",
@@ -296,13 +318,7 @@
"java.util.Optional": {
"j$.util.Optional": "java.util.Optional"
}
- },
- "retarget_method": {
- "long java.io.InputStream#transferTo(java.io.OutputStream)": "java.io.DesugarInputStream"
- },
- "amend_library_method": [
- "public long java.io.InputStream#transferTo(java.io.OutputStream)"
- ]
+ }
}
],
"shrinker_config": [
@@ -318,6 +334,7 @@
"-keepattributes Signature",
"-keepattributes EnclosingMethod",
"-keepattributes InnerClasses",
- "-dontwarn sun.misc.Unsafe"
+ "-dontwarn sun.misc.Unsafe",
+ "-dontwarn wrapper.**"
]
}
\ No newline at end of file
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json b/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
index ab3c8a6..5bf4338 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
@@ -13,16 +13,30 @@
}
},
{
+ "api_level_below_or_equal": 32,
+ "rewrite_prefix": {
+ "java.io.DesugarInputStream": "j$.io.DesugarInputStream"
+ },
+ "retarget_method_with_emulated_dispatch": {
+ "long java.io.InputStream#transferTo(java.io.OutputStream)": "java.io.DesugarInputStream"
+ },
+ "amend_library_method": [
+ "public long java.io.InputStream#transferTo(java.io.OutputStream)"
+ ]
+ },
+ {
"api_level_below_or_equal": 30,
"rewrite_prefix": {
"java.time.": "j$.time.",
"java.util.Desugar": "j$.util.Desugar"
},
"retarget_method": {
+ "java.util.TimeZone java.util.TimeZone#getTimeZone(java.time.ZoneId)": "java.util.DesugarTimeZone"
+ },
+ "retarget_method_with_emulated_dispatch": {
"java.time.Instant java.util.Date#toInstant()": "java.util.DesugarDate",
"java.time.ZoneId java.util.TimeZone#toZoneId()": "java.util.DesugarTimeZone",
- "java.time.ZonedDateTime java.util.GregorianCalendar#toZonedDateTime()": "java.util.DesugarGregorianCalendar",
- "java.util.TimeZone java.util.TimeZone#getTimeZone(java.time.ZoneId)": "java.util.DesugarTimeZone"
+ "java.time.ZonedDateTime java.util.GregorianCalendar#toZonedDateTime()": "java.util.DesugarGregorianCalendar"
},
"wrapper_conversion": [
"java.time.Clock"
@@ -73,7 +87,6 @@
"retarget_method": {
"java.util.Spliterator java.util.Arrays#spliterator(java.lang.Object[])": "java.util.DesugarArrays",
"java.util.Spliterator java.util.Arrays#spliterator(java.lang.Object[], int, int)": "java.util.DesugarArrays",
- "java.util.Spliterator java.util.LinkedHashSet#spliterator()": "java.util.DesugarLinkedHashSet",
"java.util.Spliterator$OfDouble java.util.Arrays#spliterator(double[])": "java.util.DesugarArrays",
"java.util.Spliterator$OfDouble java.util.Arrays#spliterator(double[], int, int)": "java.util.DesugarArrays",
"java.util.Spliterator$OfInt java.util.Arrays#spliterator(int[])": "java.util.DesugarArrays",
@@ -86,10 +99,13 @@
"java.util.stream.IntStream java.util.Arrays#stream(int[], int, int)": "java.util.DesugarArrays",
"java.util.stream.LongStream java.util.Arrays#stream(long[])": "java.util.DesugarArrays",
"java.util.stream.LongStream java.util.Arrays#stream(long[], int, int)": "java.util.DesugarArrays",
- "java.util.stream.Stream java.io.BufferedReader#lines()": "java.io.DesugarBufferedReader",
"java.util.stream.Stream java.util.Arrays#stream(java.lang.Object[])": "java.util.DesugarArrays",
"java.util.stream.Stream java.util.Arrays#stream(java.lang.Object[], int, int)": "java.util.DesugarArrays"
},
+ "retarget_method_with_emulated_dispatch": {
+ "java.util.stream.Stream java.io.BufferedReader#lines()": "java.io.DesugarBufferedReader",
+ "java.util.Spliterator java.util.LinkedHashSet#spliterator()": "java.util.DesugarLinkedHashSet"
+ },
"wrapper_conversion": [
"java.util.function.IntUnaryOperator",
"java.util.function.BiFunction",
@@ -115,7 +131,6 @@
"java.util.stream.Stream",
"java.util.function.ObjLongConsumer",
"java.util.function.ToDoubleFunction",
- "java.util.Spliterator",
"java.util.stream.IntStream",
"java.util.function.LongBinaryOperator",
"java.util.Spliterator$OfDouble",
@@ -142,6 +157,12 @@
"java.util.stream.BaseStream",
"java.util.function.DoublePredicate"
],
+ "wrapper_conversion_excluding": {
+ "java.util.Spliterator": [
+ "boolean java.util.Spliterator#hasCharacteristics(int)",
+ "long java.util.Spliterator#getExactSizeIfKnown()"
+ ]
+ },
"custom_conversion": {
"java.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatisticsConversions",
"java.util.IntSummaryStatistics": "java.util.IntSummaryStatisticsConversions",
@@ -259,7 +280,9 @@
"sun.util.PreHashedMap": "j$.sun.util.PreHashedMap"
},
"retarget_method": {
- "boolean java.util.Arrays#deepEquals0(java.lang.Object, java.lang.Object)": "java.util.DesugarArrays",
+ "boolean java.util.Arrays#deepEquals0(java.lang.Object, java.lang.Object)": "java.util.DesugarArrays"
+ },
+ "retarget_method_with_emulated_dispatch": {
"java.nio.file.Path java.io.File#toPath()": "java.io.DesugarFile"
},
"backport": {
@@ -275,7 +298,6 @@
{
"api_level_below_or_equal": 23,
"rewrite_prefix": {
- "java.io.DesugarInputStream": "j$.io.DesugarInputStream",
"java.lang.FunctionalInterface": "j$.lang.FunctionalInterface",
"java.nio.channels.SeekableByteChannel": "j$.nio.channels.SeekableByteChannel",
"java.util.AbstractList": "j$.util.AbstractList",
@@ -300,13 +322,7 @@
"java.util.Optional": {
"j$.util.Optional": "java.util.Optional"
}
- },
- "retarget_method": {
- "long java.io.InputStream#transferTo(java.io.OutputStream)": "java.io.DesugarInputStream"
- },
- "amend_library_method": [
- "public long java.io.InputStream#transferTo(java.io.OutputStream)"
- ]
+ }
}
],
"shrinker_config": [
@@ -322,6 +338,7 @@
"-keepattributes Signature",
"-keepattributes EnclosingMethod",
"-keepattributes InnerClasses",
- "-dontwarn sun.misc.Unsafe"
+ "-dontwarn sun.misc.Unsafe",
+ "-dontwarn wrapper.**"
]
}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/BackportedMethodList.java b/src/main/java/com/android/tools/r8/BackportedMethodList.java
index befc767..bffd580 100644
--- a/src/main/java/com/android/tools/r8/BackportedMethodList.java
+++ b/src/main/java/com/android/tools/r8/BackportedMethodList.java
@@ -79,6 +79,7 @@
return;
}
InternalOptions options = command.getInternalOptions();
+
ExecutorService executorService = ThreadUtils.getExecutorService(options);
try {
ExceptionUtils.withD8CompilationHandler(
diff --git a/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java b/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
index 9c11062..c79e8fe 100644
--- a/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
+++ b/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
@@ -106,7 +106,7 @@
InternalOptions getInternalOptions() {
InternalOptions options = new InternalOptions(factory, getReporter());
options.setMinApiLevel(AndroidApiLevel.getAndroidApiLevel(minApiLevel));
- options.setDesugaredLibrarySpecification(desugaredLibrarySpecification, getInputApp());
+ options.setDesugaredLibrarySpecification(desugaredLibrarySpecification);
return options;
}
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index a522278..b2e0f6d 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import com.android.tools.r8.dump.DumpOptions;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexItemFactory;
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 851b677..217a787 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.utils.AssertionUtils.forTesting;
import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
+import com.android.tools.r8.androidapi.ApiReferenceStubber;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.ApplicationWriter;
import com.android.tools.r8.dex.Marker;
@@ -169,9 +170,10 @@
private static AppView<AppInfo> readApp(
AndroidApp inputApp, InternalOptions options, ExecutorService executor, Timing timing)
throws IOException {
- TypeRewriter typeRewriter = options.getTypeRewriter();
ApplicationReader applicationReader = new ApplicationReader(inputApp, options, timing);
LazyLoadedDexApplication app = applicationReader.read(executor);
+ options.loadMachineDesugaredLibrarySpecification(timing, app);
+ TypeRewriter typeRewriter = options.getTypeRewriter();
AppInfo appInfo = AppInfo.createInitialAppInfo(app, applicationReader.readMainDexClasses(app));
return AppView.createForD8(appInfo, typeRewriter);
}
@@ -189,9 +191,6 @@
}
Timing timing = Timing.create("D8", options);
try {
- // Disable global optimizations.
- options.disableGlobalOptimizations();
-
// Synthetic assertion to check that testing assertions works and can be enabled.
assert forTesting(options, () -> !options.testing.testEnableTestAssertions);
@@ -267,7 +266,7 @@
namingLens = RecordRewritingNamingLens.createRecordRewritingNamingLens(appView, namingLens);
if (options.isGeneratingClassFiles()) {
- finalizeApplication(inputApp, appView, executor, namingLens);
+ finalizeApplication(appView, executor);
new CfApplicationWriter(appView, marker, GraphLens.getIdentityLens(), namingLens)
.write(options.getClassFileConsumer(), inputApp);
} else {
@@ -310,7 +309,12 @@
executor, appView.appInfo().app(), appView.appInfo().getMainDexInfo());
appView.setAppInfo(appView.appInfo().rebuildWithMainDexInfo(mainDexInfo));
}
- finalizeApplication(inputApp, appView, executor, namingLens);
+
+ finalizeApplication(appView, executor);
+
+ if (options.apiModelingOptions().enableStubbingOfClasses && !appView.options().debug) {
+ new ApiReferenceStubber(appView).run(executor);
+ }
new ApplicationWriter(
appView,
@@ -332,11 +336,7 @@
}
}
- private static void finalizeApplication(
- AndroidApp inputApp,
- AppView<AppInfo> appView,
- ExecutorService executorService,
- NamingLens namingLens)
+ private static void finalizeApplication(AppView<AppInfo> appView, ExecutorService executorService)
throws ExecutionException {
SyntheticFinalization.finalize(appView, executorService);
}
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index c8923f8..583c6bb 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.dump.DumpOptions;
import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
@@ -487,18 +488,11 @@
assert !internal.outline.enabled;
assert !internal.enableTreeShakingOfLibraryMethodOverrides;
- // TODO(b/187675788): Enable class merging for synthetics in D8.
- HorizontalClassMergerOptions horizontalClassMergerOptions =
- internal.horizontalClassMergerOptions();
- horizontalClassMergerOptions.disable();
- assert !horizontalClassMergerOptions.isEnabled(HorizontalClassMerger.Mode.INITIAL);
- assert !horizontalClassMergerOptions.isEnabled(HorizontalClassMerger.Mode.FINAL);
-
internal.desugarState = getDesugarState();
internal.encodeChecksums = getIncludeClassesChecksum();
internal.dexClassChecksumFilter = getDexClassChecksumFilter();
internal.enableInheritanceClassInDexDistributor = isOptimizeMultidexForLinearAlloc();
- internal.setDesugaredLibrarySpecification(desugaredLibrarySpecification, getInputApp());
+ internal.setDesugaredLibrarySpecification(desugaredLibrarySpecification);
internal.synthesizedClassPrefix = synthesizedClassPrefix;
internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
@@ -519,6 +513,16 @@
internal.threadCount = getThreadCount();
}
+ // Disable global optimizations.
+ internal.disableGlobalOptimizations();
+
+ // TODO(b/187675788): Enable class merging for synthetics in D8.
+ HorizontalClassMergerOptions horizontalClassMergerOptions =
+ internal.horizontalClassMergerOptions();
+ horizontalClassMergerOptions.disable();
+ assert !horizontalClassMergerOptions.isEnabled(HorizontalClassMerger.Mode.INITIAL);
+ assert !horizontalClassMergerOptions.isEnabled(HorizontalClassMerger.Mode.FINAL);
+
internal.setDumpInputFlags(getDumpInputFlags(), skipDump);
internal.dumpOptions = dumpOptions();
@@ -526,7 +530,7 @@
}
private DumpOptions dumpOptions() {
- DumpOptions.Builder builder = DumpOptions.builder(Tool.D8);
+ DumpOptions.Builder builder = DumpOptions.builder(Tool.D8).readCurrentSystemProperties();
dumpBaseCommandOptions(builder);
return builder
.setIntermediate(intermediate)
diff --git a/src/main/java/com/android/tools/r8/InputDependencyGraphConsumer.java b/src/main/java/com/android/tools/r8/InputDependencyGraphConsumer.java
new file mode 100644
index 0000000..20b0a30
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/InputDependencyGraphConsumer.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2022, 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;
+
+import com.android.tools.r8.origin.Origin;
+import java.nio.file.Path;
+
+/** Consumer for receiving file dependencies from inputs. */
+@KeepForSubclassing
+public interface InputDependencyGraphConsumer {
+
+ /**
+ * Callback indicating that file {@code dependency} is referenced from the compiler inputs.
+ *
+ * <p>Note: this callback may be called on multiple threads.
+ *
+ * <p>Note: this callback places no guarantees on order of calls or on duplicate calls.
+ *
+ * @param dependent Origin of input that is has the file {@code dependency}.
+ * @param dependency Path of the dependency.
+ */
+ void accept(Origin dependent, Path dependency);
+
+ /** Callback for proguard `-include` directive. Defaults to call general accept. */
+ default void acceptProguardInclude(Origin dependent, Path dependency) {
+ accept(dependent, dependency);
+ }
+
+ /** Callback for proguard `-injars` directive. Defaults to call general accept. */
+ default void acceptProguardInJars(Origin dependent, Path dependency) {
+ accept(dependent, dependency);
+ }
+
+ /** Callback for proguard `-libraryjars` directive. Defaults to call general accept. */
+ default void acceptProguardLibraryJars(Origin dependent, Path dependency) {
+ accept(dependent, dependency);
+ }
+
+ /** Callback for proguard `-applymapping` directive. Defaults to call general accept. */
+ default void acceptProguardApplyMapping(Origin dependent, Path dependency) {
+ accept(dependent, dependency);
+ }
+
+ /** Callback for proguard `-obfuscationdictionary` directive. Defaults to call general accept. */
+ default void acceptProguardObfuscationDictionary(Origin dependent, Path dependency) {
+ accept(dependent, dependency);
+ }
+
+ /**
+ * Callback for proguard `-classobfuscationdictionary` directive. Defaults to call general accept.
+ */
+ default void acceptProguardClassObfuscationDictionary(Origin dependent, Path dependency) {
+ accept(dependent, dependency);
+ }
+
+ /**
+ * Callback for proguard `-packageobfuscationdictionary` directive. Defaults to call general
+ * accept.
+ */
+ default void acceptProguardPackageObfuscationDictionary(Origin dependent, Path dependency) {
+ accept(dependent, dependency);
+ }
+
+ /**
+ * Callback indicating no more dependencies remain for the active compilation unit.
+ *
+ * <p>Note: this callback places no other guarantees on number of calls or on which threads.
+ */
+ void finished();
+}
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index 0c8f9ea..430d811 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -128,8 +128,6 @@
Timing timing = Timing.create("L8 desugaring", options);
assert options.cfToCfDesugar;
try {
- // Disable global optimizations.
- options.disableGlobalOptimizations();
// Since L8 Cf representation is temporary, just disable long running back-end optimizations
// on it.
options.enableLoadStoreOptimization = false;
@@ -167,7 +165,7 @@
throws IOException {
LazyLoadedDexApplication lazyApp =
new ApplicationReader(inputApp, options, timing).read(executor);
-
+ options.loadMachineDesugaredLibrarySpecification(timing, lazyApp);
TypeRewriter typeRewriter = options.getTypeRewriter();
DexApplication app = new L8TreePruner(options).prune(lazyApp, typeRewriter);
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 8678a9d..f4bb9c2 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.dump.DumpOptions;
import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
@@ -194,7 +195,7 @@
internal.enableInheritanceClassInDexDistributor = false;
assert desugaredLibrarySpecification != null;
- internal.setDesugaredLibrarySpecification(desugaredLibrarySpecification, getInputApp());
+ internal.setDesugaredLibrarySpecification(desugaredLibrarySpecification);
internal.synthesizedClassPrefix =
desugaredLibrarySpecification.getSynthesizedLibraryClassesPackagePrefix();
@@ -213,6 +214,9 @@
internal.threadCount = getThreadCount();
}
+ // Disable global optimizations.
+ internal.disableGlobalOptimizations();
+
internal.setDumpInputFlags(getDumpInputFlags(), false);
internal.dumpOptions = dumpOptions();
@@ -442,7 +446,7 @@
}
private DumpOptions dumpOptions() {
- DumpOptions.Builder builder = DumpOptions.builder(Tool.L8);
+ DumpOptions.Builder builder = DumpOptions.builder(Tool.L8).readCurrentSystemProperties();
dumpBaseCommandOptions(builder);
if (r8Command != null) {
builder.setProguardConfiguration(r8Command.getInternalOptions().getProguardConfiguration());
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 732a73b..7d177cd 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -293,6 +293,7 @@
{
ApplicationReader applicationReader = new ApplicationReader(inputApp, options, timing);
DirectMappedDexApplication application = applicationReader.read(executorService).toDirect();
+ options.loadMachineDesugaredLibrarySpecification(timing, application);
MainDexInfo mainDexInfo = applicationReader.readMainDexClassesForR8(application);
// Now that the dex-application is fully loaded, close any internal archive providers.
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 4e72575..05f03d2 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.dump.DumpOptions;
import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.experimental.startup.StartupConfiguration;
@@ -110,6 +111,7 @@
private StringConsumer proguardConfigurationConsumer = null;
private GraphConsumer keptGraphConsumer = null;
private GraphConsumer mainDexKeptGraphConsumer = null;
+ private InputDependencyGraphConsumer inputDependencyGraphConsumer = null;
private final List<FeatureSplit> featureSplits = new ArrayList<>();
private String synthesizedClassPrefix = "";
private boolean skipDump = false;
@@ -327,6 +329,19 @@
}
/**
+ * Set a consumer for receiving dependency edges for files referenced from inputs.
+ *
+ * <p>Note that these dependency edges are for files read when reading/parsing other input
+ * files, such as the proguard configuration files. For compilation dependencies for incremental
+ * desugaring see {@code setDesugarGraphConsumer}.
+ */
+ public Builder setInputDependencyGraphConsumer(
+ InputDependencyGraphConsumer inputDependencyGraphConsumer) {
+ this.inputDependencyGraphConsumer = inputDependencyGraphConsumer;
+ return self();
+ }
+
+ /**
* Set the output path-and-mode.
*
* <p>Setting the output path-and-mode will override any previous set consumer or any previous
@@ -479,7 +494,8 @@
getDesugaredLibraryConfiguration(factory, false);
ProguardConfigurationParser parser =
- new ProguardConfigurationParser(factory, reporter, allowTestProguardOptions);
+ new ProguardConfigurationParser(
+ factory, reporter, inputDependencyGraphConsumer, allowTestProguardOptions);
if (!proguardConfigs.isEmpty()) {
parser.parse(proguardConfigs);
}
@@ -592,6 +608,10 @@
getDumpInputFlags(),
getMapIdProvider(),
getSourceFileProvider());
+
+ if (inputDependencyGraphConsumer != null) {
+ inputDependencyGraphConsumer.finished();
+ }
return command;
}
@@ -943,7 +963,7 @@
internal.enableInheritanceClassInDexDistributor = isOptimizeMultidexForLinearAlloc();
- internal.setDesugaredLibrarySpecification(desugaredLibrarySpecification, getInputApp());
+ internal.setDesugaredLibrarySpecification(desugaredLibrarySpecification);
internal.synthesizedClassPrefix = synthesizedClassPrefix;
internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
@@ -991,7 +1011,7 @@
}
private DumpOptions dumpOptions() {
- DumpOptions.Builder builder = DumpOptions.builder(Tool.R8);
+ DumpOptions.Builder builder = DumpOptions.builder(Tool.R8).readCurrentSystemProperties();
dumpBaseCommandOptions(builder);
return builder
.setIncludeDataResources(includeDataResources)
diff --git a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
index ef86f73..e0852c5 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.androidapi;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DefaultInstanceInitializerCode;
import com.android.tools.r8.graph.DexClass;
@@ -106,13 +106,13 @@
}
}
- private final AppView<? extends AppInfoWithClassHierarchy> appView;
+ private final AppView<?> appView;
private final Map<DexLibraryClass, Set<DexMethod>> libraryClassesToMock =
new ConcurrentHashMap<>();
private final Set<DexType> seenTypes = Sets.newConcurrentHashSet();
private final AndroidApiLevelCompute apiLevelCompute;
- public ApiReferenceStubber(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ public ApiReferenceStubber(AppView<?> appView) {
this.appView = appView;
apiLevelCompute = appView.apiLevelCompute();
}
@@ -138,10 +138,18 @@
AppView<AppInfoWithLiveness> appInfoWithLivenessAppView = appView.withLiveness();
appInfoWithLivenessAppView.setAppInfo(
appInfoWithLivenessAppView.appInfo().rebuildWithLiveness(committedItems));
- } else {
+ } else if (appView.hasClassHierarchy()) {
appView
.withClassHierarchy()
- .setAppInfo(appView.appInfo().rebuildWithClassHierarchy(committedItems));
+ .setAppInfo(
+ appView.appInfo().withClassHierarchy().rebuildWithClassHierarchy(committedItems));
+ } else {
+ appView
+ .withoutClassHierarchy()
+ .setAppInfo(
+ new AppInfo(
+ appView.appInfo().getSyntheticItems().commit(appView.app()),
+ appView.appInfo().getMainDexInfo()));
}
}
diff --git a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
index b313b75..701d275 100644
--- a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
+++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
@@ -16,9 +16,11 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.origin.CommandLineOrigin;
import com.android.tools.r8.utils.ExceptionUtils;
+import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.MapIdTemplateProvider;
import com.android.tools.r8.utils.SourceFileTemplateProvider;
import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
@@ -46,6 +48,7 @@
public final String mainDexList;
public final MapIdProvider mapIdProvider;
public final SourceFileProvider sourceFileProvider;
+ public final String depsFileOutput;
public final List<String> proguardConfig;
public boolean printHelpAndExit;
@@ -63,6 +66,7 @@
String mainDexList,
MapIdProvider mapIdProvider,
SourceFileProvider sourceFileProvider,
+ String depsFileOutput,
boolean printHelpAndExit,
boolean disableVerticalClassMerging) {
this.output = output;
@@ -75,6 +79,7 @@
this.proguardConfig = proguardConfig;
this.mapIdProvider = mapIdProvider;
this.sourceFileProvider = sourceFileProvider;
+ this.depsFileOutput = depsFileOutput;
this.printHelpAndExit = printHelpAndExit;
this.disableVerticalClassMerging = disableVerticalClassMerging;
}
@@ -91,6 +96,7 @@
boolean printHelpAndExit = false;
MapIdProvider mapIdProvider = null;
SourceFileProvider sourceFileProvider = null;
+ String depsFileOutput = null;
// Flags to disable experimental features.
boolean disableVerticalClassMerging = false;
// These flags are currently ignored.
@@ -134,6 +140,8 @@
mapIdProvider = MapIdTemplateProvider.create(args[++i], handler);
} else if (arg.equals("--source-file-template")) {
sourceFileProvider = SourceFileTemplateProvider.create(args[++i], handler);
+ } else if (arg.equals("--deps-file")) {
+ depsFileOutput = args[++i];
} else if (arg.equals("--no-vertical-class-merging")) {
disableVerticalClassMerging = true;
} else if (arg.equals("--minimal-main-dex")) {
@@ -173,6 +181,7 @@
mainDexList,
mapIdProvider,
sourceFileProvider,
+ depsFileOutput,
printHelpAndExit,
disableVerticalClassMerging);
}
@@ -228,7 +237,13 @@
if (options.mainDexList != null) {
builder.addMainDexListFiles(Paths.get(options.mainDexList));
}
-
+ if (options.depsFileOutput != null) {
+ Path target = Paths.get(options.output);
+ if (!FileUtils.isArchive(target)) {
+ target = target.resolve("classes.dex");
+ }
+ builder.setInputDependencyGraphConsumer(new DepsFileWriter(target, options.depsFileOutput));
+ }
R8.run(builder.build());
}
diff --git a/src/main/java/com/android/tools/r8/compatproguard/DepsFileWriter.java b/src/main/java/com/android/tools/r8/compatproguard/DepsFileWriter.java
new file mode 100644
index 0000000..194bb42
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/compatproguard/DepsFileWriter.java
@@ -0,0 +1,62 @@
+// Copyright (c) 2022, 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.compatproguard;
+
+import com.android.tools.r8.InputDependencyGraphConsumer;
+import com.android.tools.r8.origin.Origin;
+import java.io.IOException;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class DepsFileWriter implements InputDependencyGraphConsumer {
+
+ private final Path dependentFile;
+ private final String dependencyOutput;
+ private final Set<Path> dependencies = new HashSet<>();
+
+ public DepsFileWriter(Path dependentFile, String dependencyOutput) {
+ this.dependentFile = dependentFile;
+ this.dependencyOutput = dependencyOutput;
+ }
+
+ @Override
+ public void accept(Origin dependent, Path dependency) {
+ dependencies.add(dependency);
+ }
+
+ @Override
+ public void finished() {
+ List<Path> sorted = new ArrayList<>(dependencies);
+ sorted.sort(Path::compareTo);
+ Path output = Paths.get(dependencyOutput);
+ try (Writer writer =
+ Files.newBufferedWriter(
+ output,
+ StandardCharsets.UTF_8,
+ StandardOpenOption.CREATE,
+ StandardOpenOption.TRUNCATE_EXISTING)) {
+ writer.write(escape(dependentFile.toString()));
+ writer.write(":");
+ for (Path path : sorted) {
+ writer.write(" ");
+ writer.write(escape(path.toString()));
+ }
+ writer.write("\n");
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static String escape(String filepath) {
+ return filepath.replace(" ", "\\ ");
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/dump/CompilerDump.java b/src/main/java/com/android/tools/r8/dump/CompilerDump.java
new file mode 100644
index 0000000..a303c14
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dump/CompilerDump.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2022, 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.dump;
+
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.ZipUtils;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public class CompilerDump {
+
+ private final Path directory;
+
+ public static CompilerDump fromArchive(Path dumpArchive, Path dumpExtractionDirectory)
+ throws IOException {
+ ZipUtils.unzip(dumpArchive, dumpExtractionDirectory);
+ return new CompilerDump(dumpExtractionDirectory);
+ }
+
+ public CompilerDump(Path directory) {
+ this.directory = directory;
+ }
+
+ public Path getProgramArchive() {
+ return directory.resolve("program.jar");
+ }
+
+ public Path getClasspathArchive() {
+ return directory.resolve("classpath.jar");
+ }
+
+ public Path getLibraryArchive() {
+ return directory.resolve("library.jar");
+ }
+
+ public Path getBuildPropertiesFile() {
+ return directory.resolve("build.properties");
+ }
+
+ public Path getProguardConfigFile() {
+ return directory.resolve("proguard.config");
+ }
+
+ public DumpOptions getBuildProperties() throws IOException {
+ if (Files.exists(getBuildPropertiesFile())) {
+ DumpOptions.Builder builder = new DumpOptions.Builder();
+ DumpOptions.parse(
+ FileUtils.readTextFile(getBuildPropertiesFile(), StandardCharsets.UTF_8), builder);
+ return builder.build();
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/DumpOptions.java b/src/main/java/com/android/tools/r8/dump/DumpOptions.java
similarity index 72%
rename from src/main/java/com/android/tools/r8/DumpOptions.java
rename to src/main/java/com/android/tools/r8/dump/DumpOptions.java
index 2726086..224d24a 100644
--- a/src/main/java/com/android/tools/r8/DumpOptions.java
+++ b/src/main/java/com/android/tools/r8/dump/DumpOptions.java
@@ -1,9 +1,10 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2022, 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;
+package com.android.tools.r8.dump;
+import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.experimental.startup.StartupConfiguration;
import com.android.tools.r8.features.FeatureSplitConfiguration;
@@ -12,7 +13,10 @@
import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.ThreadUtils;
+import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@@ -55,6 +59,8 @@
private final ProguardConfiguration proguardConfiguration;
private final List<ProguardConfigurationRule> mainDexKeepRules;
+ private final Map<String, String> systemProperties;
+
// Reporting only.
private final boolean dumpInputToFile;
@@ -74,6 +80,7 @@
FeatureSplitConfiguration featureSplitConfiguration,
ProguardConfiguration proguardConfiguration,
List<ProguardConfigurationRule> mainDexKeepRules,
+ Map<String, String> systemProperties,
boolean dumpInputToFile) {
this.tool = tool;
this.compilationMode = compilationMode;
@@ -90,6 +97,7 @@
this.featureSplitConfiguration = featureSplitConfiguration;
this.proguardConfiguration = proguardConfiguration;
this.mainDexKeepRules = mainDexKeepRules;
+ this.systemProperties = systemProperties;
this.dumpInputToFile = dumpInputToFile;
}
@@ -112,18 +120,93 @@
addOptionalDumpEntry(builder, TREE_SHAKING_KEY, treeShaking);
addOptionalDumpEntry(builder, MINIFICATION_KEY, minification);
addOptionalDumpEntry(builder, FORCE_PROGUARD_COMPATIBILITY_KEY, forceProguardCompatibility);
- System.getProperties()
- .stringPropertyNames()
- .forEach(
- name -> {
- if (name.startsWith("com.android.tools.r8.")) {
- String value = System.getProperty(name);
- addDumpEntry(builder, SYSTEM_PROPERTY_PREFIX + name, value);
- }
- });
+ ArrayList<String> sortedKeys = new ArrayList<>(systemProperties.keySet());
+ sortedKeys.sort(String::compareTo);
+ sortedKeys.forEach(
+ key -> addDumpEntry(builder, SYSTEM_PROPERTY_PREFIX + key, systemProperties.get(key)));
return builder.toString();
}
+ public static void parse(String content, DumpOptions.Builder builder) {
+ String[] lines = content.split("\n");
+ for (String line : lines) {
+ String trimmed = line.trim();
+ int i = trimmed.indexOf('=');
+ if (i < 0) {
+ throw new RuntimeException("Invalid dump line. Expected = in line: '" + trimmed + "'");
+ }
+ String key = trimmed.substring(0, i).trim();
+ String value = trimmed.substring(i + 1).trim();
+ parseKeyValue(builder, key, value);
+ }
+ }
+
+ private static void parseKeyValue(Builder builder, String key, String value) {
+ switch (key) {
+ case TOOL_KEY:
+ builder.setTool(Tool.valueOf(value));
+ return;
+ case MODE_KEY:
+ if (value.equals(DEBUG_MODE_VALUE)) {
+ builder.setCompilationMode(CompilationMode.DEBUG);
+ } else if (value.equals(RELEASE_MODE_VALUE)) {
+ builder.setCompilationMode(CompilationMode.RELEASE);
+ } else {
+ parseKeyValueError(key, value);
+ }
+ return;
+ case MIN_API_KEY:
+ builder.setMinApi(Integer.parseInt(value));
+ return;
+ case OPTIMIZE_MULTIDEX_FOR_LINEAR_ALLOC_KEY:
+ builder.setOptimizeMultidexForLinearAlloc(Boolean.parseBoolean(value));
+ return;
+ case THREAD_COUNT_KEY:
+ builder.setThreadCount(Integer.parseInt(value));
+ return;
+ case DESUGAR_STATE_KEY:
+ builder.setDesugarState(DesugarState.valueOf(value));
+ return;
+ case INTERMEDIATE_KEY:
+ builder.setIntermediate(Boolean.parseBoolean(value));
+ return;
+ case INCLUDE_DATA_RESOURCES_KEY:
+ builder.setIncludeDataResources(Optional.of(Boolean.parseBoolean(value)));
+ return;
+ case TREE_SHAKING_KEY:
+ builder.setTreeShaking(Boolean.parseBoolean(value));
+ return;
+ case MINIFICATION_KEY:
+ builder.setMinification(Boolean.parseBoolean(value));
+ return;
+ case FORCE_PROGUARD_COMPATIBILITY_KEY:
+ builder.setForceProguardCompatibility(Boolean.parseBoolean(value));
+ return;
+ default:
+ if (key.startsWith(SYSTEM_PROPERTY_PREFIX)) {
+ builder.setSystemProperty(key.substring(SYSTEM_PROPERTY_PREFIX.length()), value);
+ } else {
+ parseKeyValueError(key, value);
+ }
+ }
+ }
+
+ private static void parseKeyValueError(String key, String value) {
+ throw new RuntimeException("Unknown key value pair: " + key + " = " + value);
+ }
+
+ public Tool getTool() {
+ return tool;
+ }
+
+ public CompilationMode getCompilationMode() {
+ return compilationMode;
+ }
+
+ public int getMinApi() {
+ return minApi;
+ }
+
private void addOptionalDumpEntry(StringBuilder builder, String key, Optional<?> optionalValue) {
optionalValue.ifPresent(bool -> addDumpEntry(builder, key, bool));
}
@@ -169,11 +252,11 @@
}
public static Builder builder(Tool tool) {
- return new Builder(tool);
+ return new Builder().setTool(tool);
}
public static class Builder {
- private final Tool tool;
+ private Tool tool;
private CompilationMode compilationMode;
private int minApi;
private boolean optimizeMultidexForLinearAlloc;
@@ -190,11 +273,16 @@
private ProguardConfiguration proguardConfiguration;
private List<ProguardConfigurationRule> mainDexKeepRules;
+ private Map<String, String> systemProperties = new HashMap<>();
+
// Reporting only.
private boolean dumpInputToFile;
- public Builder(Tool tool) {
+ public Builder() {}
+
+ public Builder setTool(Tool tool) {
this.tool = tool;
+ return this;
}
public Builder setCompilationMode(CompilationMode compilationMode) {
@@ -274,7 +362,26 @@
return this;
}
+ public Builder setSystemProperty(String key, String value) {
+ this.systemProperties.put(key, value);
+ return this;
+ }
+
+ public Builder readCurrentSystemProperties() {
+ System.getProperties()
+ .stringPropertyNames()
+ .forEach(
+ name -> {
+ if (name.startsWith("com.android.tools.r8.")) {
+ String value = System.getProperty(name);
+ setSystemProperty(name, value);
+ }
+ });
+ return this;
+ }
+
public DumpOptions build() {
+ assert tool != null;
return new DumpOptions(
tool,
compilationMode,
@@ -291,6 +398,7 @@
featureSplitConfiguration,
proguardConfiguration,
mainDexKeepRules,
+ systemProperties,
dumpInputToFile);
}
}
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 6f1ab90..5b97ef1 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -31,6 +31,7 @@
import com.android.tools.r8.ir.optimize.library.LibraryMethodSideEffectModelCollection;
import com.android.tools.r8.naming.SeedMapper;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
+import com.android.tools.r8.optimize.interfaces.collection.OpenClosedInterfacesCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepClassInfo;
import com.android.tools.r8.shaking.KeepFieldInfo;
@@ -105,6 +106,8 @@
private HorizontallyMergedClasses horizontallyMergedClasses = HorizontallyMergedClasses.empty();
private VerticallyMergedClasses verticallyMergedClasses;
private EnumDataMap unboxedEnums = null;
+ private OpenClosedInterfacesCollection openClosedInterfacesCollection =
+ OpenClosedInterfacesCollection.getDefault();
// TODO(b/169115389): Remove
private Set<DexMethod> cfByteCodePassThrough = ImmutableSet.of();
private Map<DexType, DexValueString> sourceDebugExtensions = new IdentityHashMap<>();
@@ -610,6 +613,15 @@
testing().verticallyMergedClassesConsumer.accept(dexItemFactory(), verticallyMergedClasses);
}
+ public OpenClosedInterfacesCollection getOpenClosedInterfacesCollection() {
+ return openClosedInterfacesCollection;
+ }
+
+ public void setOpenClosedInterfacesCollection(
+ OpenClosedInterfacesCollection openClosedInterfacesCollection) {
+ this.openClosedInterfacesCollection = openClosedInterfacesCollection;
+ }
+
public boolean hasUnboxedEnums() {
return unboxedEnums != null;
}
@@ -713,6 +725,8 @@
if (hasMainDexRootSet()) {
setMainDexRootSet(mainDexRootSet.withoutPrunedItems(prunedItems));
}
+ setOpenClosedInterfacesCollection(
+ openClosedInterfacesCollection.withoutPrunedItems(prunedItems));
}
@SuppressWarnings("unchecked")
@@ -805,6 +819,8 @@
if (appView.hasMainDexRootSet()) {
appView.setMainDexRootSet(appView.getMainDexRootSet().rewrittenWithLens(lens));
}
+ appView.setOpenClosedInterfacesCollection(
+ appView.getOpenClosedInterfacesCollection().rewrittenWithLens(lens));
if (appView.hasRootSet()) {
appView.setRootSet(appView.rootSet().rewrittenWithLens(lens));
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 8315b20..547fd3f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -150,21 +150,17 @@
}
public boolean isAlwaysNull(AppView<AppInfoWithLiveness> appView) {
- return isAlwaysNull(appView.appInfo());
- }
-
- public boolean isAlwaysNull(AppInfoWithLiveness appInfo) {
if (!isClassType()) {
return false;
}
- DexProgramClass clazz = asProgramClassOrNull(appInfo.definitionFor(this));
+ DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(this));
if (clazz == null) {
return false;
}
- if (appInfo.options().enableUninstantiatedTypeOptimizationForInterfaces) {
- return !appInfo.isInstantiatedDirectlyOrIndirectly(clazz);
+ if (clazz.isInterface() && appView.getOpenClosedInterfacesCollection().isMaybeOpen(clazz)) {
+ return false;
}
- return !clazz.isInterface() && !appInfo.isInstantiatedDirectlyOrIndirectly(clazz);
+ return !appView.appInfo().isInstantiatedDirectlyOrIndirectly(clazz);
}
public boolean isSamePackage(DexType other) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java b/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java
index b6d9757..bff5528 100644
--- a/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java
@@ -4,86 +4,39 @@
package com.android.tools.r8.graph;
+import com.android.tools.r8.ir.analysis.type.ArrayTypeElement;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.InterfaceCollection;
-import com.android.tools.r8.utils.ObjectUtils;
-import java.util.Iterator;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.google.common.collect.Iterables;
public class DexTypeUtils {
public static DexType computeLeastUpperBound(
AppView<? extends AppInfoWithClassHierarchy> appView, Iterable<DexType> types) {
- DexItemFactory dexItemFactory = appView.dexItemFactory();
-
- Iterator<DexType> iterator = types.iterator();
- assert iterator.hasNext();
-
- DexType result = iterator.next();
- while (iterator.hasNext()) {
- result = computeLeastUpperBound(appView, result, iterator.next());
- }
- return result;
+ TypeElement join =
+ TypeElement.join(Iterables.transform(types, type -> type.toTypeElement(appView)), appView);
+ return toDexType(appView, join);
}
- public static DexType computeLeastUpperBound(
- AppView<? extends AppInfoWithClassHierarchy> appView, DexType type, DexType other) {
- if (type == other) {
- return type;
- }
-
+ private static DexType toDexType(
+ AppView<? extends AppInfoWithClassHierarchy> appView, TypeElement type) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
- if (type == dexItemFactory.objectType
- || other == dexItemFactory.objectType
- || type.isArrayType() != other.isArrayType()) {
- return dexItemFactory.objectType;
+ if (type.isPrimitiveType()) {
+ return type.asPrimitiveType().toDexType(dexItemFactory);
}
-
if (type.isArrayType()) {
- assert other.isArrayType();
- int arrayDimension = type.getNumberOfLeadingSquareBrackets();
- if (other.getNumberOfLeadingSquareBrackets() != arrayDimension) {
- return dexItemFactory.objectType;
- }
-
- DexType baseType = type.toBaseType(dexItemFactory);
- DexType otherBaseType = other.toBaseType(dexItemFactory);
- if (baseType.isPrimitiveType() || otherBaseType.isPrimitiveType()) {
- assert baseType != otherBaseType;
- return dexItemFactory.objectType;
- }
-
- return dexItemFactory.createArrayType(
- arrayDimension, computeLeastUpperBound(appView, baseType, otherBaseType));
+ ArrayTypeElement arrayType = type.asArrayType();
+ DexType baseType = toDexType(appView, arrayType.getBaseType());
+ return baseType.toArrayType(arrayType.getNesting(), dexItemFactory);
}
-
- assert !type.isArrayType();
- assert !other.isArrayType();
-
- boolean isInterface =
- type.isClassType()
- && ObjectUtils.getBooleanOrElse(
- appView.definitionFor(type), DexClass::isInterface, false);
- boolean otherIsInterface =
- other.isClassType()
- && ObjectUtils.getBooleanOrElse(
- appView.definitionFor(other), DexClass::isInterface, false);
- if (isInterface != otherIsInterface) {
- return dexItemFactory.objectType;
+ assert type.isClassType();
+ ClassTypeElement classType = type.asClassType();
+ if (classType.getClassType() != dexItemFactory.objectType) {
+ return classType.getClassType();
}
-
- if (isInterface) {
- assert otherIsInterface;
- InterfaceCollection interfaceCollection =
- ClassTypeElement.computeLeastUpperBoundOfInterfaces(
- appView, InterfaceCollection.singleton(type), InterfaceCollection.singleton(other));
- return interfaceCollection.hasSingleKnownInterface()
- ? interfaceCollection.getSingleKnownInterface()
- : dexItemFactory.objectType;
+ if (classType.getInterfaces().hasSingleKnownInterface()) {
+ return classType.getInterfaces().getSingleKnownInterface();
}
-
- assert !isInterface;
- assert !otherIsInterface;
-
- return ClassTypeElement.computeLeastUpperBoundOfClasses(appView.appInfo(), type, other);
+ return dexItemFactory.objectType;
}
}
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 4b79710..70d534e 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -279,6 +279,7 @@
classMergers.add(
new ClassMerger.Builder(appView, codeProvider, group, mode).build(lensBuilder));
}
+ appView.dexItemFactory().clearTypeElementsCache();
return classMergers;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java
index f60f332..329d43a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.cf.code.CfLoad;
import com.android.tools.r8.cf.code.CfPosition;
import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.cf.code.CfSafeCheckCast;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
@@ -115,7 +116,13 @@
// Assign each field.
addCfInstructionsForInstanceFieldAssignments(
- instructionBuilder, instanceFieldAssignmentsPre, argumentToLocalIndex, maxStack, lens);
+ appView,
+ method,
+ instructionBuilder,
+ instanceFieldAssignmentsPre,
+ argumentToLocalIndex,
+ maxStack,
+ lens);
// Load receiver for parent constructor call.
int stackHeightForParentConstructorCall = 1;
@@ -155,7 +162,13 @@
// Assign each field.
addCfInstructionsForInstanceFieldAssignments(
- instructionBuilder, instanceFieldAssignmentsPost, argumentToLocalIndex, maxStack, lens);
+ appView,
+ method,
+ instructionBuilder,
+ instanceFieldAssignmentsPost,
+ argumentToLocalIndex,
+ maxStack,
+ lens);
// Return.
instructionBuilder.add(new CfReturnVoid());
@@ -174,6 +187,8 @@
}
private static void addCfInstructionsForInstanceFieldAssignments(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ ProgramMethod method,
ImmutableList.Builder<CfInstruction> instructionBuilder,
Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignments,
int[] argumentToLocalIndex,
@@ -186,8 +201,28 @@
int stackSizeForInitializationInfo =
addCfInstructionsForInitializationInfo(
instructionBuilder, initializationInfo, argumentToLocalIndex, field.getType());
- instructionBuilder.add(
- new CfInstanceFieldWrite(lens.getRenamedFieldSignature(field, lens.getPrevious())));
+ DexField rewrittenField = lens.getRenamedFieldSignature(field, lens.getPrevious());
+
+ // Insert a check to ensure the program continues to type check according to Java type
+ // checking. Otherwise, instance initializer merging may cause open interfaces. If
+ // <init>(A) and <init>(B) both have the behavior `this.i = arg; this.j = arg` where the
+ // type of `i` is I and the type of `j` is J, and both A and B implements I and J, then
+ // the constructors are merged into a single constructor <init>(java.lang.Object), which
+ // is no longer strictly type checking. Note that no choice of parameter type would solve
+ // this.
+ if (initializationInfo.isArgumentInitializationInfo()) {
+ int argumentIndex =
+ initializationInfo.asArgumentInitializationInfo().getArgumentIndex();
+ if (argumentIndex > 0) {
+ DexType argumentType = method.getArgumentType(argumentIndex);
+ if (argumentType.isClassType()
+ && !appView.appInfo().isSubtype(argumentType, rewrittenField.getType())) {
+ instructionBuilder.add(new CfSafeCheckCast(rewrittenField.getType()));
+ }
+ }
+ }
+
+ instructionBuilder.add(new CfInstanceFieldWrite(rewrittenField));
maxStack.setMax(stackSizeForInitializationInfo + 1);
});
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
index 1c3443b..f669473 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
@@ -27,14 +27,15 @@
import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.structural.Ordered;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
public class InstanceInitializerMerger {
@@ -101,10 +102,6 @@
return classFileVersion;
}
- private DexMethod getNewMethodReference(ProgramMethod representative) {
- return getNewMethodReference(representative, false);
- }
-
private DexMethod getNewMethodReference(ProgramMethod representative, boolean needsClassId) {
DexType[] oldParameters = representative.getParameters().values;
DexType[] newParameters =
@@ -112,12 +109,15 @@
System.arraycopy(oldParameters, 0, newParameters, 0, oldParameters.length);
for (int i = 0; i < oldParameters.length; i++) {
final int parameterIndex = i;
- newParameters[i] =
- DexTypeUtils.computeLeastUpperBound(
- appView,
- Iterables.transform(
- instanceInitializers,
- instanceInitializer -> instanceInitializer.getParameter(parameterIndex)));
+ Set<DexType> parameterTypes =
+ SetUtils.newIdentityHashSet(
+ builder ->
+ instanceInitializers.forEach(
+ instanceInitializer ->
+ builder.accept(instanceInitializer.getParameter(parameterIndex))));
+ if (parameterTypes.size() > 1) {
+ newParameters[i] = DexTypeUtils.computeLeastUpperBound(appView, parameterTypes);
+ }
}
if (needsClassId) {
assert ArrayUtils.last(newParameters) == null;
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 67cc625..080df47 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -293,6 +293,13 @@
virtuallyMergedMethodsKeepInfo.amendKeepInfo(appView.getKeepInfo(oldMethod));
}
+ // The super method reference is not guaranteed to be rebound to a definition. To ensure correct
+ // lens code rewriting we need to disable proto normalization until lens code rewriting no
+ // longer relies on member rebinding (b/182129249).
+ if (superMethod != null) {
+ virtuallyMergedMethodsKeepInfo.getKeepInfo().disallowParameterReordering();
+ }
+
// Add a mapping from a synthetic name to the synthetic merged method.
lensBuilder.recordNewMethodSignature(bridgeMethodReference, newMethodReference);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
index 5d34c84..04b738c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
@@ -351,7 +351,7 @@
}
// Record access.
- if (field.isProgramField() && appView.appInfo().mayPropagateValueFor(field)) {
+ if (field.isProgramField() && appView.appInfo().mayPropagateValueFor(appView, field)) {
if (field.getAccessFlags().isStatic() == isStatic) {
if (isWrite) {
recordFieldAccessContext(definition, writtenFields, readFields);
@@ -396,7 +396,7 @@
private void recordAccessThatCannotBeOptimized(
DexClassAndField field, DexEncodedField definition) {
constantFields.remove(definition);
- if (field.isProgramField() && appView.appInfo().mayPropagateValueFor(field)) {
+ if (field.isProgramField() && appView.appInfo().mayPropagateValueFor(appView, field)) {
destroyFieldAccessContexts(definition);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/InterfaceCollection.java b/src/main/java/com/android/tools/r8/ir/analysis/type/InterfaceCollection.java
index 7b80c82..02c35fd 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/InterfaceCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/InterfaceCollection.java
@@ -16,6 +16,8 @@
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
public class InterfaceCollection {
@@ -118,7 +120,25 @@
}
public void forEach(BiConsumer<DexType, Boolean> fn) {
- interfaces.forEach(fn::accept);
+ interfaces.forEach(fn);
+ }
+
+ public void forEachKnownInterface(Consumer<DexType> consumer) {
+ forEach(
+ (type, isKnown) -> {
+ if (isKnown) {
+ consumer.accept(type);
+ }
+ });
+ }
+
+ public boolean allKnownInterfacesMatch(Predicate<DexType> fn) {
+ for (Entry<DexType> entry : interfaces.reference2BooleanEntrySet()) {
+ if (entry.getBooleanValue() && !fn.test(entry.getKey())) {
+ return false;
+ }
+ }
+ return true;
}
public boolean anyMatch(BiPredicate<DexType, Boolean> fn) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
index d06fd44..879e5a3 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.Lists;
import java.util.ArrayDeque;
import java.util.Comparator;
@@ -162,25 +163,26 @@
}
public static DexType getRefinedReceiverType(
- AppView<? extends AppInfoWithClassHierarchy> appView, InvokeMethodWithReceiver invoke) {
- return getRefinedReceiverType(appView, invoke.getInvokedMethod(), invoke.getReceiver());
- }
-
- public static DexType getRefinedReceiverType(
- AppView<? extends AppInfoWithClassHierarchy> appView, DexMethod method, Value receiver) {
- return toRefinedReceiverType(receiver.getDynamicUpperBoundType(appView), method, appView);
+ AppView<AppInfoWithLiveness> appView, InvokeMethodWithReceiver invoke) {
+ return toRefinedReceiverType(
+ invoke.getReceiver().getDynamicType(appView), invoke.getInvokedMethod(), appView);
}
public static DexType toRefinedReceiverType(
- TypeElement receiverUpperBoundType,
+ DynamicType dynamicReceiverType,
DexMethod method,
AppView<? extends AppInfoWithClassHierarchy> appView) {
- DexType staticReceiverType = method.holder;
- if (receiverUpperBoundType.isClassType()) {
- ClassTypeElement classType = receiverUpperBoundType.asClassType();
- DexType refinedType = classType.getClassType();
+ DexType staticReceiverType = method.getHolderType();
+ TypeElement staticReceiverTypeElement = staticReceiverType.toTypeElement(appView);
+ TypeElement dynamicReceiverUpperBoundType =
+ dynamicReceiverType.getDynamicUpperBoundType(staticReceiverTypeElement);
+ if (dynamicReceiverUpperBoundType.isClassType()) {
+ ClassTypeElement dynamicReceiverUpperBoundClassType =
+ dynamicReceiverUpperBoundType.asClassType();
+ DexType refinedType = dynamicReceiverUpperBoundClassType.getClassType();
if (refinedType == appView.dexItemFactory().objectType) {
- DexType singleKnownInterface = classType.getInterfaces().getSingleKnownInterface();
+ DexType singleKnownInterface =
+ dynamicReceiverUpperBoundClassType.getInterfaces().getSingleKnownInterface();
if (singleKnownInterface != null) {
refinedType = singleKnownInterface;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index 864e37e..5e96f46 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -164,6 +164,11 @@
return true;
}
}
+ if (!appView
+ .getOpenClosedInterfacesCollection()
+ .isDefinitelyInstanceOfStaticType(appViewWithLiveness, object())) {
+ return true;
+ }
TypeElement castType = TypeElement.fromDexType(type, definitelyNotNull(), appView);
if (object()
.getDynamicUpperBoundType(appViewWithLiveness)
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 062220b..f57b79f 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
@@ -20,8 +20,7 @@
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
import com.android.tools.r8.ir.analysis.modeling.LibraryMethodReadSetModeling;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult;
@@ -133,10 +132,7 @@
@Override
public DexClassAndMethod lookupSingleTarget(
- AppView<?> appView,
- ProgramMethod context,
- TypeElement receiverUpperBoundType,
- ClassTypeElement receiverLowerBoundType) {
+ AppView<?> appView, ProgramMethod context, DynamicType dynamicReceiverType) {
DexMethod invokedMethod = getInvokedMethod();
DexEncodedMethod result;
if (appView.appInfo().hasLiveness()) {
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 153ca1b..6749695 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
@@ -4,7 +4,6 @@
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;
@@ -17,8 +16,7 @@
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -98,10 +96,7 @@
@Override
public DexClassAndMethod lookupSingleTarget(
- AppView<?> appView,
- ProgramMethod context,
- TypeElement receiverUpperBoundType,
- ClassTypeElement receiverLowerBoundType) {
+ AppView<?> appView, ProgramMethod context, DynamicType dynamicReceiverType) {
if (!appView.appInfo().hasLiveness()) {
return null;
}
@@ -110,13 +105,12 @@
appViewWithLiveness
.appInfo()
.lookupSingleVirtualTarget(
+ appViewWithLiveness,
getInvokedMethod(),
context,
true,
appView,
- toRefinedReceiverType(
- receiverUpperBoundType, getInvokedMethod(), appViewWithLiveness),
- receiverLowerBoundType);
+ dynamicReceiverType);
return asDexClassAndMethodOrNull(result, appView);
}
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 36ec641..d01bcaa 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
@@ -15,6 +15,7 @@
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.optimize.DefaultInliningOracle;
@@ -37,18 +38,6 @@
return Iterables.skip(arguments(), 1);
}
- public boolean hasRefinedReceiverLowerBoundType(AppView<AppInfoWithLiveness> appView) {
- assert isInvokeMethodWithDynamicDispatch();
- return getReceiver().getDynamicLowerBoundType(appView) != null;
- }
-
- public boolean hasRefinedReceiverUpperBoundType(AppView<AppInfoWithLiveness> appView) {
- assert isInvokeMethodWithDynamicDispatch();
- DexType staticReceiverType = getInvokedMethod().holder;
- DexType refinedReceiverType = TypeAnalysis.getRefinedReceiverType(appView, this);
- return refinedReceiverType != staticReceiverType;
- }
-
@Override
public boolean isInvokeMethodWithReceiver() {
return true;
@@ -77,29 +66,20 @@
@Override
public final DexClassAndMethod lookupSingleTarget(AppView<?> appView, ProgramMethod context) {
- TypeElement receiverUpperBoundType = null;
- ClassTypeElement receiverLowerBoundType = null;
- if (appView.enableWholeProgramOptimizations()) {
- AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
- receiverUpperBoundType = getReceiver().getDynamicUpperBoundType(appViewWithLiveness);
- receiverLowerBoundType = getReceiver().getDynamicLowerBoundType(appViewWithLiveness);
- }
- return lookupSingleTarget(appView, context, receiverUpperBoundType, receiverLowerBoundType);
+ DynamicType dynamicReceiverType =
+ appView.hasLiveness()
+ ? getReceiver().getDynamicType(appView.withLiveness())
+ : DynamicType.unknown();
+ return lookupSingleTarget(appView, context, dynamicReceiverType);
}
public abstract DexClassAndMethod lookupSingleTarget(
- AppView<?> appView,
- ProgramMethod context,
- TypeElement receiverUpperBoundType,
- ClassTypeElement receiverLowerBoundType);
+ AppView<?> appView, ProgramMethod context, DynamicType dynamicReceiverType);
public final ProgramMethod lookupSingleProgramTarget(
- AppView<?> appView,
- ProgramMethod context,
- TypeElement receiverUpperBoundType,
- ClassTypeElement receiverLowerBoundType) {
+ AppView<?> appView, ProgramMethod context, DynamicType dynamicReceiverType) {
return DexClassAndMethod.asProgramMethodOrNull(
- lookupSingleTarget(appView, context, receiverUpperBoundType, receiverLowerBoundType));
+ lookupSingleTarget(appView, context, dynamicReceiverType));
}
@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 630e177..d41559e 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
@@ -14,8 +14,7 @@
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -105,10 +104,7 @@
@Override
public DexClassAndMethod lookupSingleTarget(
- AppView<?> appView,
- ProgramMethod context,
- TypeElement receiverUpperBoundType,
- ClassTypeElement receiverLowerBoundType) {
+ AppView<?> appView, ProgramMethod context, DynamicType dynamicReceiverType) {
if (appView.appInfo().hasLiveness() && context != null) {
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
AppInfoWithLiveness appInfo = appViewWithLiveness.appInfo();
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 b5933de..d0bca6a 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
@@ -4,7 +4,6 @@
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;
@@ -18,8 +17,7 @@
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -103,19 +101,14 @@
@Override
public DexClassAndMethod lookupSingleTarget(
- AppView<?> appView,
- ProgramMethod context,
- TypeElement receiverUpperBoundType,
- ClassTypeElement receiverLowerBoundType) {
- return lookupSingleTarget(
- appView, context, receiverUpperBoundType, receiverLowerBoundType, getInvokedMethod());
+ AppView<?> appView, ProgramMethod context, DynamicType dynamicReceiverType) {
+ return lookupSingleTarget(appView, context, dynamicReceiverType, getInvokedMethod());
}
public static DexClassAndMethod lookupSingleTarget(
AppView<?> appView,
ProgramMethod context,
- TypeElement receiverUpperBoundType,
- ClassTypeElement receiverLowerBoundType,
+ DynamicType dynamicReceiverType,
DexMethod method) {
DexEncodedMethod result = null;
if (appView.appInfo().hasLiveness()) {
@@ -124,12 +117,7 @@
appViewWithLiveness
.appInfo()
.lookupSingleVirtualTarget(
- method,
- context,
- false,
- appView,
- toRefinedReceiverType(receiverUpperBoundType, method, appViewWithLiveness),
- receiverLowerBoundType);
+ appViewWithLiveness, method, context, false, appView, dynamicReceiverType);
} else {
// In D8, allow lookupSingleTarget() to be used for finding final library methods. This is
// used for library modeling.
diff --git a/src/main/java/com/android/tools/r8/ir/code/Pop.java b/src/main/java/com/android/tools/r8/ir/code/Pop.java
index bf8ee2d..01bdc96 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Pop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Pop.java
@@ -53,7 +53,19 @@
@Override
public boolean identicalNonValueNonPositionParts(Instruction other) {
- return other.isPop();
+ if (!other.isPop()) {
+ return false;
+ }
+ Pop pop = other.asPop();
+ if (getFirstOperand().isDefinedByInstructionSatisfying(Instruction::isInitClass)) {
+ InitClass initClass = getFirstOperand().getDefinition().asInitClass();
+ if (!pop.getFirstOperand().isDefinedByInstructionSatisfying(Instruction::isInitClass)) {
+ return false;
+ }
+ InitClass otherInitClass = pop.getFirstOperand().getDefinition().asInitClass();
+ return initClass.getClassValue() == otherInitClass.getClassValue();
+ }
+ return !pop.getFirstOperand().isDefinedByInstructionSatisfying(Instruction::isInitClass);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 0ac2896..9cf8f31 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -98,6 +98,8 @@
import com.android.tools.r8.naming.IdentifierNameStringMarker;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorIROptimizer;
+import com.android.tools.r8.optimize.interfaces.analysis.OpenClosedInterfacesAnalysis;
+import com.android.tools.r8.optimize.interfaces.analysis.OpenClosedInterfacesAnalysisImpl;
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepMethodInfo;
@@ -156,6 +158,7 @@
private final ServiceLoaderRewriter serviceLoaderRewriter;
private final EnumValueOptimizer enumValueOptimizer;
private final EnumUnboxer enumUnboxer;
+ private final OpenClosedInterfacesAnalysis openClosedInterfacesAnalysis;
public final AssumeInserter assumeInserter;
private final DynamicTypeOptimization dynamicTypeOptimization;
@@ -241,6 +244,7 @@
this.methodOptimizationInfoCollector = null;
this.enumValueOptimizer = null;
this.enumUnboxer = EnumUnboxer.empty();
+ this.openClosedInterfacesAnalysis = OpenClosedInterfacesAnalysis.empty();
this.assumeInserter = null;
return;
}
@@ -274,6 +278,7 @@
this.memberValuePropagation = new MemberValuePropagation(appViewWithLiveness);
this.methodOptimizationInfoCollector =
new MethodOptimizationInfoCollector(appViewWithLiveness, this);
+ this.openClosedInterfacesAnalysis = new OpenClosedInterfacesAnalysisImpl(appViewWithLiveness);
if (options.isMinifying()) {
this.identifierNameStringMarker = new IdentifierNameStringMarker(appViewWithLiveness);
} else {
@@ -305,6 +310,7 @@
this.methodOptimizationInfoCollector = null;
this.enumValueOptimizer = null;
this.enumUnboxer = EnumUnboxer.empty();
+ this.openClosedInterfacesAnalysis = OpenClosedInterfacesAnalysis.empty();
}
this.stringSwitchRemover =
options.isStringSwitchConversionEnabled()
@@ -655,6 +661,7 @@
appView.withArgumentPropagator(
argumentPropagator -> argumentPropagator.initializeCodeScanner(executorService, timing));
enumUnboxer.prepareForPrimaryOptimizationPass(graphLensForPrimaryOptimizationPass);
+ openClosedInterfacesAnalysis.prepareForPrimaryOptimizationPass();
outliner.prepareForPrimaryOptimizationPass(graphLensForPrimaryOptimizationPass);
if (fieldAccessAnalysis != null) {
@@ -841,6 +848,7 @@
if (inliner != null) {
inliner.onLastWaveDone(postMethodProcessorBuilder, executorService, timing);
}
+ openClosedInterfacesAnalysis.onPrimaryOptimizationPassComplete();
}
public void addWaveDoneAction(com.android.tools.r8.utils.Action action) {
@@ -1158,6 +1166,8 @@
assert code.verifyTypes(appView);
assert code.isConsistentSSA();
+ openClosedInterfacesAnalysis.analyze(context, code);
+
if (shouldPassThrough(context)) {
// If the code is pass trough, do not finalize by overwriting the existing code.
assert appView.enableWholeProgramOptimizations();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/IRProcessingCallGraphUseRegistry.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/IRProcessingCallGraphUseRegistry.java
index e20123e..00c9b70 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/IRProcessingCallGraphUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/IRProcessingCallGraphUseRegistry.java
@@ -102,6 +102,12 @@
private void processInitClass(DexType type) {
DexType rewrittenType = appView.graphLens().lookupType(type);
+ if (rewrittenType.isIntType()) {
+ // Type was unboxed; init-class instruction will be removed by enum unboxer.
+ assert appView.hasUnboxedEnums();
+ assert appView.unboxedEnums().isUnboxedEnum(type);
+ return;
+ }
DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(rewrittenType));
if (clazz == null) {
assert false;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java
index a817cd6..4b15bbc 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java
@@ -85,7 +85,7 @@
}
} else {
ProgramMethod singleTarget =
- appView.appInfo().lookupSingleProgramTarget(type, method, context, appView);
+ appView.appInfo().lookupSingleProgramTarget(appView, type, method, context, appView);
if (singleTarget != null) {
processSingleTarget(singleTarget, context);
}
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 68d48dc..65f5e7d 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
@@ -100,13 +100,14 @@
public static List<DexMethod> generateListOfBackportedMethods(
AndroidApp androidApp, InternalOptions options, ExecutorService executor) throws IOException {
List<DexMethod> methods = new ArrayList<>();
- TypeRewriter typeRewriter = options.getTypeRewriter();
AppInfo appInfo = null;
if (androidApp != null) {
DexApplication app =
new ApplicationReader(androidApp, options, Timing.empty()).read(executor);
+ options.loadMachineDesugaredLibrarySpecification(Timing.empty(), app);
appInfo = AppInfo.createInitialAppInfo(app);
}
+ TypeRewriter typeRewriter = options.getTypeRewriter();
AppView<?> appView = AppView.createForD8(appInfo, typeRewriter);
BackportedMethodRewriter.RewritableMethods rewritableMethods =
new BackportedMethodRewriter.RewritableMethods(options, appView);
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 a014283..3d91587 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
@@ -23,7 +23,6 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexValue.DexValueNull;
import com.android.tools.r8.graph.FieldAccessFlags;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.MethodResolutionResult;
@@ -34,14 +33,11 @@
import com.android.tools.r8.ir.desugar.lambda.ForcefullyMovedLambdaMethodConsumer;
import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring;
import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring.DesugarInvoke;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
-import org.objectweb.asm.Opcodes;
/**
* Represents lambda class generated for a lambda descriptor in context of lambda instantiation
@@ -64,15 +60,11 @@
public static final String JAVAC_EXPECTED_LAMBDA_METHOD_PREFIX = "lambda$";
public static final String R8_LAMBDA_ACCESSOR_METHOD_PREFIX = "$r8$lambda$";
- private static final OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
-
final AppView<?> appView;
final LambdaInstructionDesugaring desugaring;
public final DexType type;
public LambdaDescriptor descriptor;
public final DexMethod constructor;
- final DexMethod classConstructor;
- public final DexField lambdaField;
public final Target target;
// Considered final but is set after due to circularity in allocation.
@@ -99,14 +91,6 @@
this.target = createTarget(accessedFrom);
- boolean stateless = isStateless();
- this.classConstructor =
- !stateless
- ? null
- : factory.createMethod(type, constructorProto, factory.classConstructorMethodName);
- this.lambdaField =
- !stateless ? null : factory.createField(type, type, factory.lambdaInstanceFieldName);
-
// Synthesize the program class once all fields are set.
synthesizeLambdaClass(builder, desugarInvoke);
}
@@ -130,13 +114,12 @@
private void synthesizeLambdaClass(
SyntheticProgramClassBuilder builder, DesugarInvoke desugarInvoke) {
builder.setInterfaces(descriptor.interfaces);
- synthesizeStaticFields(builder);
synthesizeInstanceFields(builder);
synthesizeDirectMethods(builder);
synthesizeVirtualMethods(builder, desugarInvoke);
}
- final DexField getCaptureField(int index) {
+ DexField getCaptureField(int index) {
return appView
.dexItemFactory()
.createField(
@@ -145,10 +128,6 @@
appView.dexItemFactory().createString("f$" + index));
}
- public final boolean isStateless() {
- return descriptor.isStateless();
- }
-
// Synthesize virtual methods.
private void synthesizeVirtualMethods(
SyntheticProgramClassBuilder builder, DesugarInvoke desugarInvoke) {
@@ -193,38 +172,19 @@
// Synthesize direct methods.
private void synthesizeDirectMethods(SyntheticProgramClassBuilder builder) {
- boolean stateless = isStateless();
- List<DexEncodedMethod> methods = new ArrayList<>(stateless ? 2 : 1);
-
// Constructor.
MethodAccessFlags accessFlags =
MethodAccessFlags.fromSharedAccessFlags(
- (stateless ? Constants.ACC_PRIVATE : Constants.ACC_PUBLIC) | Constants.ACC_SYNTHETIC,
- true);
- methods.add(
+ Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, true);
+ DexEncodedMethod method =
DexEncodedMethod.syntheticBuilder()
.setMethod(constructor)
.setAccessFlags(accessFlags)
.setCode(LambdaConstructorSourceCode.build(this))
// The api level is computed when tracing.
.disableAndroidApiLevelCheck()
- .build());
-
- // Class constructor for stateless lambda classes.
- if (stateless) {
- methods.add(
- DexEncodedMethod.syntheticBuilder()
- .setMethod(classConstructor)
- .setAccessFlags(
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_SYNTHETIC | Constants.ACC_STATIC, true))
- .setCode(LambdaClassConstructorSourceCode.build(this))
- // The api level is computed when tracing.
- .disableAndroidApiLevelCheck()
- .build());
- feedback.classInitializerMayBePostponed(methods.get(1));
- }
- builder.setDirectMethods(methods);
+ .build();
+ builder.setDirectMethods(Collections.singletonList(method));
}
// Synthesize instance fields to represent captured values.
@@ -247,43 +207,6 @@
builder.setInstanceFields(fields);
}
- // Synthesize static fields to represent singleton instance.
- private void synthesizeStaticFields(SyntheticProgramClassBuilder builder) {
- if (isStateless()) {
- // Create instance field for stateless lambda.
- assert this.lambdaField != null;
- builder.setStaticFields(
- Collections.singletonList(
- DexEncodedField.syntheticBuilder()
- .setField(this.lambdaField)
- .setAccessFlags(
- FieldAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC
- | Constants.ACC_FINAL
- | Constants.ACC_SYNTHETIC
- | Constants.ACC_STATIC))
- .setStaticValue(DexValueNull.NULL)
- // The api level is computed when tracing.
- .disableAndroidApiLevelCheck()
- .build()));
- }
- }
-
- public static int getAsmOpcodeForInvokeType(MethodHandleType type) {
- switch (type) {
- case INVOKE_INTERFACE:
- return Opcodes.INVOKEINTERFACE;
- case INVOKE_STATIC:
- return Opcodes.INVOKESTATIC;
- case INVOKE_DIRECT:
- return Opcodes.INVOKESPECIAL;
- case INVOKE_INSTANCE:
- return Opcodes.INVOKEVIRTUAL;
- default:
- throw new Unreachable("Unexpected method handle type: " + type);
- }
- }
-
// Creates a delegation target for this particular lambda class. Note that we
// should always be able to create targets for the lambdas we support.
private Target createTarget(ProgramMethod accessedFrom) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
deleted file mode 100644
index ddcb68d..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.desugar;
-
-import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.cf.code.CfNew;
-import com.android.tools.r8.cf.code.CfReturnVoid;
-import com.android.tools.r8.cf.code.CfStackInstruction;
-import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
-import com.android.tools.r8.cf.code.CfStaticFieldWrite;
-import com.android.tools.r8.graph.CfCode;
-import com.google.common.collect.ImmutableList;
-import org.objectweb.asm.Opcodes;
-
-// Source code representing synthesized lambda class constructor.
-// Used for stateless lambdas to instantiate singleton instance.
-final class LambdaClassConstructorSourceCode {
-
- public static CfCode build(LambdaClass lambda) {
- int maxStack = 2;
- int maxLocals = 0;
- return new CfCode(
- lambda.type,
- maxStack,
- maxLocals,
- ImmutableList.of(
- new CfNew(lambda.type),
- new CfStackInstruction(Opcode.Dup),
- new CfInvoke(Opcodes.INVOKESPECIAL, lambda.constructor, false),
- new CfStaticFieldWrite(lambda.lambdaField, lambda.lambdaField),
- new CfReturnVoid()));
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
index bc7a544..e72af99 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -190,15 +190,6 @@
}
}
- public Iterable<DexType> getReferencedBaseTypes(DexItemFactory dexItemFactory) {
- return enforcedProto.getBaseTypes(dexItemFactory);
- }
-
- /** Is a stateless lambda, i.e. lambda does not capture any values */
- final boolean isStateless() {
- return captures.isEmpty();
- }
-
/** Checks if call site needs a accessor when referenced from `accessedFrom`. */
boolean needsAccessor(ProgramMethod accessedFrom) {
if (implHandle.type.isInvokeInterface()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java
index 9d3bf12..08b98e6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java
@@ -4,9 +4,9 @@
package com.android.tools.r8.ir.desugar.desugaredlibrary;
+import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
import java.io.IOException;
@@ -35,8 +35,8 @@
AndroidApiLevel getRequiredCompilationApiLevel();
- MachineDesugaredLibrarySpecification toMachineSpecification(
- InternalOptions options, AndroidApp app, Timing timing) throws IOException;
+ MachineDesugaredLibrarySpecification toMachineSpecification(DexApplication app, Timing timing)
+ throws IOException;
MachineDesugaredLibrarySpecification toMachineSpecification(
InternalOptions options, Path library, Timing timing, Path desugaredJDKLib)
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
index 238ae7b..2a5f49f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
@@ -3,11 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification;
+import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.HumanToMachineSpecificationConverter;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
import java.io.IOException;
@@ -91,8 +91,8 @@
@Override
public MachineDesugaredLibrarySpecification toMachineSpecification(
- InternalOptions options, AndroidApp app, Timing timing) throws IOException {
- return new HumanToMachineSpecificationConverter(timing).convert(this, app, options);
+ DexApplication app, Timing timing) {
+ return new HumanToMachineSpecificationConverter(timing).convert(this, app);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
index e52cc02..2eb550b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.google.common.collect.Sets;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@@ -26,6 +27,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
import java.util.function.Consumer;
public class HumanDesugaredLibrarySpecificationParser {
@@ -43,9 +45,12 @@
static final String API_LEVEL_BELOW_OR_EQUAL_KEY = "api_level_below_or_equal";
static final String WRAPPER_CONVERSION_KEY = "wrapper_conversion";
+ static final String WRAPPER_CONVERSION_EXCLUDING_KEY = "wrapper_conversion_excluding";
static final String CUSTOM_CONVERSION_KEY = "custom_conversion";
static final String REWRITE_PREFIX_KEY = "rewrite_prefix";
static final String RETARGET_METHOD_KEY = "retarget_method";
+ static final String RETARGET_METHOD_EMULATED_DISPATCH_KEY =
+ "retarget_method_with_emulated_dispatch";
static final String REWRITE_DERIVED_PREFIX_KEY = "rewrite_derived_prefix";
static final String EMULATE_INTERFACE_KEY = "emulate_interface";
static final String DONT_REWRITE_KEY = "dont_rewrite";
@@ -248,6 +253,14 @@
stringDescriptorToDexType(retarget.getValue().getAsString()));
}
}
+ if (jsonFlagSet.has(RETARGET_METHOD_EMULATED_DISPATCH_KEY)) {
+ for (Map.Entry<String, JsonElement> retarget :
+ jsonFlagSet.get(RETARGET_METHOD_EMULATED_DISPATCH_KEY).getAsJsonObject().entrySet()) {
+ builder.retargetMethodEmulatedDispatch(
+ parseMethod(retarget.getKey()),
+ stringDescriptorToDexType(retarget.getValue().getAsString()));
+ }
+ }
if (jsonFlagSet.has(BACKPORT_KEY)) {
for (Map.Entry<String, JsonElement> backport :
jsonFlagSet.get(BACKPORT_KEY).getAsJsonObject().entrySet()) {
@@ -277,6 +290,14 @@
builder.addWrapperConversion(stringDescriptorToDexType(wrapper.getAsString()));
}
}
+ if (jsonFlagSet.has(WRAPPER_CONVERSION_EXCLUDING_KEY)) {
+ for (Map.Entry<String, JsonElement> wrapper :
+ jsonFlagSet.get(WRAPPER_CONVERSION_EXCLUDING_KEY).getAsJsonObject().entrySet()) {
+ builder.addWrapperConversion(
+ stringDescriptorToDexType(wrapper.getKey()),
+ parseMethods(wrapper.getValue().getAsJsonArray()));
+ }
+ }
if (jsonFlagSet.has(DONT_REWRITE_KEY)) {
JsonArray dontRewrite = jsonFlagSet.get(DONT_REWRITE_KEY).getAsJsonArray();
for (JsonElement rewrite : dontRewrite) {
@@ -298,6 +319,14 @@
}
}
+ private Set<DexMethod> parseMethods(JsonArray array) {
+ Set<DexMethod> methods = Sets.newIdentityHashSet();
+ for (JsonElement method : array) {
+ methods.add(parseMethod(method.getAsString()));
+ }
+ return methods;
+ }
+
private DexMethod parseMethod(String signature) {
methodParser.parseMethod(signature);
return methodParser.getMethod();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
index ece6fe0..9eb3ed7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
@@ -14,6 +14,7 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;
+import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
@@ -26,11 +27,12 @@
private final Map<String, Map<String, String>> rewriteDerivedPrefix;
private final Map<DexType, DexType> emulatedInterfaces;
private final Map<DexMethod, DexType> retargetMethod;
+ private final Map<DexMethod, DexType> retargetMethodEmulatedDispatch;
private final Map<DexType, DexType> legacyBackport;
private final Map<DexType, DexType> customConversions;
private final Set<DexMethod> dontRewriteInvocation;
private final Set<DexType> dontRetarget;
- private final Set<DexType> wrapperConversions;
+ private final Map<DexType, Set<DexMethod>> wrapperConversions;
private final Map<DexMethod, MethodAccessFlags> amendLibraryMethod;
HumanRewritingFlags(
@@ -38,16 +40,18 @@
Map<String, Map<String, String>> rewriteDerivedPrefix,
Map<DexType, DexType> emulateLibraryInterface,
Map<DexMethod, DexType> retargetMethod,
+ Map<DexMethod, DexType> retargetMethodEmulatedDispatch,
Map<DexType, DexType> legacyBackport,
Map<DexType, DexType> customConversion,
Set<DexMethod> dontRewriteInvocation,
Set<DexType> dontRetarget,
- Set<DexType> wrapperConversion,
+ Map<DexType, Set<DexMethod>> wrapperConversion,
Map<DexMethod, MethodAccessFlags> amendLibraryMethod) {
this.rewritePrefix = rewritePrefix;
this.rewriteDerivedPrefix = rewriteDerivedPrefix;
this.emulatedInterfaces = emulateLibraryInterface;
this.retargetMethod = retargetMethod;
+ this.retargetMethodEmulatedDispatch = retargetMethodEmulatedDispatch;
this.legacyBackport = legacyBackport;
this.customConversions = customConversion;
this.dontRewriteInvocation = dontRewriteInvocation;
@@ -64,9 +68,10 @@
ImmutableMap.of(),
ImmutableMap.of(),
ImmutableMap.of(),
+ ImmutableMap.of(),
ImmutableSet.of(),
ImmutableSet.of(),
- ImmutableSet.of(),
+ ImmutableMap.of(),
ImmutableMap.of());
}
@@ -82,6 +87,7 @@
rewriteDerivedPrefix,
emulatedInterfaces,
retargetMethod,
+ retargetMethodEmulatedDispatch,
legacyBackport,
customConversions,
dontRewriteInvocation,
@@ -106,6 +112,10 @@
return retargetMethod;
}
+ public Map<DexMethod, DexType> getRetargetMethodEmulatedDispatch() {
+ return retargetMethodEmulatedDispatch;
+ }
+
public Map<DexType, DexType> getLegacyBackport() {
return legacyBackport;
}
@@ -122,7 +132,7 @@
return dontRetarget;
}
- public Set<DexType> getWrapperConversions() {
+ public Map<DexType, Set<DexMethod>> getWrapperConversions() {
return wrapperConversions;
}
@@ -146,11 +156,12 @@
private final Map<String, Map<String, String>> rewriteDerivedPrefix;
private final Map<DexType, DexType> emulatedInterfaces;
private final Map<DexMethod, DexType> retargetMethod;
+ private final Map<DexMethod, DexType> retargetMethodEmulatedDispatch;
private final Map<DexType, DexType> legacyBackport;
private final Map<DexType, DexType> customConversions;
private final Set<DexMethod> dontRewriteInvocation;
private final Set<DexType> dontRetarget;
- private final Set<DexType> wrapperConversions;
+ private final Map<DexType, Set<DexMethod>> wrapperConversions;
private final Map<DexMethod, MethodAccessFlags> amendLibraryMethod;
Builder(Reporter reporter, Origin origin) {
@@ -163,9 +174,10 @@
new IdentityHashMap<>(),
new IdentityHashMap<>(),
new IdentityHashMap<>(),
+ new IdentityHashMap<>(),
Sets.newIdentityHashSet(),
Sets.newIdentityHashSet(),
- Sets.newIdentityHashSet(),
+ new IdentityHashMap<>(),
new IdentityHashMap<>());
}
@@ -175,27 +187,28 @@
Map<String, String> rewritePrefix,
Map<String, Map<String, String>> rewriteDerivedPrefix,
Map<DexType, DexType> emulateLibraryInterface,
- Map<DexMethod, DexType> retargetCoreLibMember,
+ Map<DexMethod, DexType> retargetMethod,
+ Map<DexMethod, DexType> retargetMethodEmulatedDispatch,
Map<DexType, DexType> backportCoreLibraryMember,
Map<DexType, DexType> customConversions,
Set<DexMethod> dontRewriteInvocation,
Set<DexType> dontRetargetLibMember,
- Set<DexType> wrapperConversions,
+ Map<DexType, Set<DexMethod>> wrapperConversions,
Map<DexMethod, MethodAccessFlags> amendLibrary) {
this.reporter = reporter;
this.origin = origin;
this.rewritePrefix = new HashMap<>(rewritePrefix);
this.rewriteDerivedPrefix = new HashMap<>(rewriteDerivedPrefix);
this.emulatedInterfaces = new IdentityHashMap<>(emulateLibraryInterface);
- this.retargetMethod = new IdentityHashMap<>(retargetCoreLibMember);
+ this.retargetMethod = new IdentityHashMap<>(retargetMethod);
+ this.retargetMethodEmulatedDispatch = new IdentityHashMap<>(retargetMethodEmulatedDispatch);
this.legacyBackport = new IdentityHashMap<>(backportCoreLibraryMember);
this.customConversions = new IdentityHashMap<>(customConversions);
this.dontRewriteInvocation = Sets.newIdentityHashSet();
this.dontRewriteInvocation.addAll(dontRewriteInvocation);
this.dontRetarget = Sets.newIdentityHashSet();
this.dontRetarget.addAll(dontRetargetLibMember);
- this.wrapperConversions = Sets.newIdentityHashSet();
- this.wrapperConversions.addAll(wrapperConversions);
+ this.wrapperConversions = new IdentityHashMap<>(wrapperConversions);
this.amendLibraryMethod = new IdentityHashMap<>(amendLibrary);
}
@@ -255,7 +268,11 @@
}
public Builder addWrapperConversion(DexType dexType) {
- wrapperConversions.add(dexType);
+ return addWrapperConversion(dexType, Collections.emptySet());
+ }
+
+ public Builder addWrapperConversion(DexType dexType, Set<DexMethod> excludedMethods) {
+ wrapperConversions.put(dexType, excludedMethods);
return this;
}
@@ -268,6 +285,15 @@
return this;
}
+ public Builder retargetMethodEmulatedDispatch(DexMethod key, DexType rewrittenType) {
+ put(
+ retargetMethodEmulatedDispatch,
+ key,
+ rewrittenType,
+ HumanDesugaredLibrarySpecificationParser.RETARGET_METHOD_EMULATED_DISPATCH_KEY);
+ return this;
+ }
+
public Builder putLegacyBackport(DexType backportType, DexType rewrittenBackportType) {
put(
legacyBackport,
@@ -299,16 +325,18 @@
ImmutableMap.copyOf(rewriteDerivedPrefix),
ImmutableMap.copyOf(emulatedInterfaces),
ImmutableMap.copyOf(retargetMethod),
+ ImmutableMap.copyOf(retargetMethodEmulatedDispatch),
ImmutableMap.copyOf(legacyBackport),
ImmutableMap.copyOf(customConversions),
ImmutableSet.copyOf(dontRewriteInvocation),
ImmutableSet.copyOf(dontRetarget),
- ImmutableSet.copyOf(wrapperConversions),
+ ImmutableMap.copyOf(wrapperConversions),
ImmutableMap.copyOf(amendLibraryMethod));
}
private void validate() {
- SetView<DexType> dups = Sets.intersection(customConversions.keySet(), wrapperConversions);
+ SetView<DexType> dups =
+ Sets.intersection(customConversions.keySet(), wrapperConversions.keySet());
if (!dups.isEmpty()) {
throw reporter.fatalError(
new StringDiagnostic(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java
index afc7dd6..c059c27 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java
@@ -96,6 +96,11 @@
commonBuilder::retargetMethod,
builder::retargetMethod);
deduplicateFlags(
+ flags.getRetargetMethodEmulatedDispatch(),
+ otherFlags.getRetargetMethodEmulatedDispatch(),
+ commonBuilder::retargetMethodEmulatedDispatch,
+ builder::retargetMethodEmulatedDispatch);
+ deduplicateFlags(
flags.getLegacyBackport(),
otherFlags.getLegacyBackport(),
commonBuilder::putLegacyBackport,
@@ -116,15 +121,31 @@
otherFlags.getDontRetarget(),
commonBuilder::addDontRetargetLibMember,
builder::addDontRetargetLibMember);
- deduplicateFlags(
- flags.getWrapperConversions(),
- otherFlags.getWrapperConversions(),
- commonBuilder::addWrapperConversion,
- builder::addWrapperConversion);
+
+ deduplicateWrapperFlags(flags, otherFlags, commonBuilder, builder);
deduplicateAmendLibraryMemberFlags(flags, otherFlags, commonBuilder, builder);
}
+ private static void deduplicateWrapperFlags(
+ HumanRewritingFlags flags,
+ HumanRewritingFlags otherFlags,
+ HumanRewritingFlags.Builder commonBuilder,
+ HumanRewritingFlags.Builder builder) {
+ Map<DexType, Set<DexMethod>> other = otherFlags.getWrapperConversions();
+ flags
+ .getWrapperConversions()
+ .forEach(
+ (wrapperType, excludedMethods) -> {
+ if (other.containsKey(wrapperType)) {
+ assert excludedMethods.equals(other.get(wrapperType));
+ commonBuilder.addWrapperConversion(wrapperType, excludedMethods);
+ } else {
+ builder.addWrapperConversion(wrapperType, excludedMethods);
+ }
+ });
+ }
+
private static void deduplicateAmendLibraryMemberFlags(
HumanRewritingFlags flags,
HumanRewritingFlags otherFlags,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
index 97749d6..928eeab 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
@@ -18,12 +18,14 @@
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.LIBRARY_FLAGS_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.PROGRAM_FLAGS_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.REQUIRED_COMPILATION_API_LEVEL_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.RETARGET_METHOD_EMULATED_DISPATCH_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.RETARGET_METHOD_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.REWRITE_DERIVED_PREFIX_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.REWRITE_PREFIX_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.SHRINKER_CONFIG_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.SUPPORT_ALL_CALLBACKS_FROM_LIBRARY_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.SYNTHESIZED_LIBRARY_CLASSES_PACKAGE_PREFIX_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.WRAPPER_CONVERSION_EXCLUDING_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.WRAPPER_CONVERSION_KEY;
import com.android.tools.r8.DiagnosticsHandler;
@@ -33,7 +35,6 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodAccessFlags;
-import com.google.common.collect.Sets;
import com.google.gson.Gson;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import java.util.ArrayList;
@@ -107,6 +108,11 @@
if (!flags.getRetargetMethod().isEmpty()) {
toJson.put(RETARGET_METHOD_KEY, mapToString(flags.getRetargetMethod()));
}
+ if (!flags.getRetargetMethodEmulatedDispatch().isEmpty()) {
+ toJson.put(
+ RETARGET_METHOD_EMULATED_DISPATCH_KEY,
+ mapToString(flags.getRetargetMethodEmulatedDispatch()));
+ }
if (!flags.getDontRetarget().isEmpty()) {
toJson.put(DONT_RETARGET_KEY, setToString(flags.getDontRetarget()));
}
@@ -114,7 +120,7 @@
toJson.put(BACKPORT_KEY, mapToString(flags.getLegacyBackport()));
}
if (!flags.getWrapperConversions().isEmpty()) {
- toJson.put(WRAPPER_CONVERSION_KEY, setToString(flags.getWrapperConversions()));
+ registerWrapperConversions(toJson, flags.getWrapperConversions());
}
if (!flags.getCustomConversions().isEmpty()) {
toJson.put(CUSTOM_CONVERSION_KEY, mapToString(flags.getCustomConversions()));
@@ -127,15 +133,31 @@
return list;
}
- private Set<String> amendLibraryToString(Map<DexMethod, MethodAccessFlags> amendLibraryMembers) {
- Set<String> stringSet = Sets.newHashSet();
+ private void registerWrapperConversions(
+ Map<String, Object> toJson, Map<DexType, Set<DexMethod>> wrapperConversions) {
+ List<String> stringSet = new ArrayList<>();
+ Map<String, List<String>> stringMap = new TreeMap<>();
+ wrapperConversions.forEach(
+ (k, v) -> {
+ if (v.isEmpty()) {
+ stringSet.add(toString(k));
+ } else {
+ stringMap.put(toString(k), setToString(v));
+ }
+ });
+ toJson.put(WRAPPER_CONVERSION_KEY, stringSet);
+ toJson.put(WRAPPER_CONVERSION_EXCLUDING_KEY, stringMap);
+ }
+
+ private List<String> amendLibraryToString(Map<DexMethod, MethodAccessFlags> amendLibraryMembers) {
+ List<String> stringSet = new ArrayList<>();
amendLibraryMembers.forEach(
(member, flags) -> stringSet.add(flags.toString() + " " + toString(member)));
return stringSet;
}
- private Set<String> setToString(Set<? extends DexItem> set) {
- Set<String> stringSet = Sets.newHashSet();
+ private List<String> setToString(Set<? extends DexItem> set) {
+ List<String> stringSet = new ArrayList<>();
set.forEach(e -> stringSet.add(toString(e)));
return stringSet;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
index 3daf522..01fda4a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
@@ -13,7 +14,6 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LegacyToHumanSpecificationConverter;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
import java.io.IOException;
@@ -119,10 +119,10 @@
@Override
public MachineDesugaredLibrarySpecification toMachineSpecification(
- InternalOptions options, AndroidApp app, Timing timing) throws IOException {
+ DexApplication app, Timing timing) throws IOException {
return new LegacyToHumanSpecificationConverter(timing)
- .convert(this, app, options)
- .toMachineSpecification(options, app, timing);
+ .convert(this, app)
+ .toMachineSpecification(app, timing);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/AppForSpecConversion.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/AppForSpecConversion.java
index 45f9dae..0df69b0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/AppForSpecConversion.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/AppForSpecConversion.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion;
-import com.android.tools.r8.ClassFileResourceProvider;
-import com.android.tools.r8.ProgramResourceProvider;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.utils.AndroidApp;
@@ -17,27 +15,8 @@
import java.util.concurrent.ExecutorService;
public class AppForSpecConversion {
- static DexApplication readApp(
- AndroidApp inputApp, InternalOptions options, boolean libraryCompilation, Timing timing)
- throws IOException {
- timing.begin("Read App");
- AndroidApp.Builder builder = AndroidApp.builder();
- for (ClassFileResourceProvider classFileResourceProvider :
- inputApp.getLibraryResourceProviders()) {
- builder.addLibraryResourceProvider(classFileResourceProvider);
- }
- if (libraryCompilation) {
- for (ProgramResourceProvider programResourceProvider :
- inputApp.getProgramResourceProviders()) {
- builder.addProgramResourceProvider(programResourceProvider);
- }
- }
- DexApplication app = internalReadApp(builder.build(), options, timing);
- timing.end();
- return app;
- }
- static DexApplication readAppForTesting(
+ public static DexApplication readAppForTesting(
Path desugaredJDKLib,
Path androidLib,
InternalOptions options,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
index 198fbc2..182202b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
@@ -93,7 +93,8 @@
List<DexType> subInterfaces = emulatedInterfaceHierarchy.get(method.getHolderType());
LinkedHashMap<DexType, DerivedMethod> extraDispatchCases = new LinkedHashMap<>();
// Retarget core lib emulated dispatch handled as part of emulated interface dispatch.
- Map<DexMethod, DexType> retargetCoreLibMember = rewritingFlags.getRetargetMethod();
+ Map<DexMethod, DexType> retargetCoreLibMember =
+ rewritingFlags.getRetargetMethodEmulatedDispatch();
for (DexMethod retarget : retargetCoreLibMember.keySet()) {
if (retarget.match(method)) {
DexClass inClass = appInfo.definitionFor(retarget.getHolderType());
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
index e0ae560..87c03e3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -22,6 +23,7 @@
private final AppInfoWithClassHierarchy appInfo;
private final MachineRewritingFlags.Builder builder;
private final String synthesizedPrefix;
+ private final boolean libraryCompilation;
private final Map<DexString, DexString> descriptorPrefix;
private final Map<DexString, Map<DexString, DexString>> descriptorDifferentPrefix;
private final Set<DexString> usedPrefix = Sets.newIdentityHashSet();
@@ -29,11 +31,12 @@
public HumanToMachinePrefixConverter(
AppInfoWithClassHierarchy appInfo,
MachineRewritingFlags.Builder builder,
- String synthesizedPrefix,
+ HumanDesugaredLibrarySpecification humanSpec,
HumanRewritingFlags rewritingFlags) {
this.appInfo = appInfo;
this.builder = builder;
- this.synthesizedPrefix = synthesizedPrefix;
+ this.synthesizedPrefix = humanSpec.getSynthesizedLibraryClassesPackagePrefix();
+ this.libraryCompilation = humanSpec.isLibraryCompilation();
this.descriptorPrefix = convertRewritePrefix(rewritingFlags.getRewritePrefix());
this.descriptorDifferentPrefix =
convertRewriteDifferentPrefix(rewritingFlags.getRewriteDerivedPrefix());
@@ -43,9 +46,10 @@
HumanRewritingFlags rewritingFlags, BiConsumer<String, Set<DexString>> warnConsumer) {
rewriteClasses();
rewriteValues(rewritingFlags.getRetargetMethod());
+ rewriteValues(rewritingFlags.getRetargetMethodEmulatedDispatch());
rewriteValues(rewritingFlags.getCustomConversions());
rewriteEmulatedInterface(rewritingFlags.getEmulatedInterfaces());
- rewriteRetargetKeys(rewritingFlags.getRetargetMethod());
+ rewriteRetargetKeys(rewritingFlags.getRetargetMethodEmulatedDispatch());
warnIfUnusedPrefix(warnConsumer);
}
@@ -88,7 +92,9 @@
private void rewriteClasses() {
appInfo.app().forEachLibraryType(this::registerClassType);
- appInfo.app().forEachProgramType(this::registerClassType);
+ if (libraryCompilation) {
+ appInfo.app().forEachProgramType(this::registerClassType);
+ }
}
private void registerClassType(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
index a5a27aa..6db6518 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
@@ -38,13 +38,16 @@
BiConsumer<String, Set<? extends DexReference>> warnConsumer) {
rewritingFlags
.getRetargetMethod()
+ .forEach((method, type) -> convertRetargetMethod(builder, method, type));
+ rewritingFlags
+ .getRetargetMethodEmulatedDispatch()
.forEach(
(method, type) ->
- convertRetargetCoreLibMemberFlag(builder, rewritingFlags, method, type));
+ convertRetargetMethodEmulatedDispatch(builder, rewritingFlags, method, type));
warnConsumer.accept("Cannot retarget missing methods: ", missingMethods);
}
- private void convertRetargetCoreLibMemberFlag(
+ private void convertRetargetMethodEmulatedDispatch(
MachineRewritingFlags.Builder builder,
HumanRewritingFlags rewritingFlags,
DexMethod method,
@@ -56,14 +59,53 @@
return;
}
if (foundMethod.isStatic()) {
+ appInfo
+ .app()
+ .options
+ .reporter
+ .error("Cannot generate emulated dispatch for static method " + foundMethod);
+ return;
+ }
+ if (!seemsToNeedEmulatedDispatch(holder, foundMethod)) {
+ appInfo
+ .app()
+ .options
+ .reporter
+ .warning(
+ "Generating (seemingly unnecessary) emulated dispatch for final method "
+ + foundMethod);
+ }
+ convertEmulatedVirtualRetarget(builder, rewritingFlags, foundMethod, type);
+ }
+
+ private void convertRetargetMethod(
+ MachineRewritingFlags.Builder builder, DexMethod method, DexType type) {
+ DexClass holder = appInfo.definitionFor(method.holder);
+ DexEncodedMethod foundMethod = holder.lookupMethod(method);
+ if (foundMethod == null) {
+ missingMethods.add(method);
+ return;
+ }
+ if (foundMethod.isStatic()) {
convertStaticRetarget(builder, foundMethod, type);
return;
}
- if (holder.isFinal() || foundMethod.isFinal()) {
- convertNonEmulatedVirtualRetarget(builder, foundMethod, type);
- return;
+ if (seemsToNeedEmulatedDispatch(holder, foundMethod)) {
+ appInfo
+ .app()
+ .options
+ .reporter
+ .warning(
+ "Retargeting non final method "
+ + foundMethod
+ + " which could lead to invalid runtime execution in overrides.");
}
- convertEmulatedVirtualRetarget(builder, rewritingFlags, foundMethod, type);
+ convertNonEmulatedVirtualRetarget(builder, foundMethod, type);
+ }
+
+ private boolean seemsToNeedEmulatedDispatch(DexClass holder, DexEncodedMethod method) {
+ assert !method.isStatic();
+ return !(holder.isFinal() || method.isFinal());
}
private void convertEmulatedVirtualRetarget(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
index c0bcd82..5ea225d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
@@ -21,7 +21,6 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineTopLevelFlags;
-import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.Timing;
@@ -43,14 +42,6 @@
this.timing = timing;
}
- public MachineDesugaredLibrarySpecification convert(
- HumanDesugaredLibrarySpecification humanSpec, AndroidApp inputApp, InternalOptions options)
- throws IOException {
- DexApplication app =
- AppForSpecConversion.readApp(inputApp, options, humanSpec.isLibraryCompilation(), timing);
- return convert(humanSpec, app);
- }
-
public MachineDesugaredLibrarySpecification convertForTesting(
HumanDesugaredLibrarySpecification humanSpec,
Path desugaredJDKLib,
@@ -63,7 +54,7 @@
return convert(humanSpec, app);
}
- private MachineDesugaredLibrarySpecification convert(
+ public MachineDesugaredLibrarySpecification convert(
HumanDesugaredLibrarySpecification humanSpec, DexApplication app) {
timing.begin("Human to machine convert");
reporter = app.options.reporter;
@@ -72,9 +63,7 @@
app,
humanSpec.isLibraryCompilation(),
humanSpec.getTopLevelFlags().getRequiredCompilationAPILevel());
- MachineRewritingFlags machineRewritingFlags =
- convertRewritingFlags(
- humanSpec.getSynthesizedLibraryClassesPackagePrefix(), humanSpec.getRewritingFlags());
+ MachineRewritingFlags machineRewritingFlags = convertRewritingFlags(humanSpec);
MachineTopLevelFlags topLevelFlags = convertTopLevelFlags(humanSpec.getTopLevelFlags());
timing.end();
return new MachineDesugaredLibrarySpecification(
@@ -92,8 +81,9 @@
}
private MachineRewritingFlags convertRewritingFlags(
- String synthesizedPrefix, HumanRewritingFlags rewritingFlags) {
+ HumanDesugaredLibrarySpecification humanSpec) {
timing.begin("convert rewriting flags");
+ HumanRewritingFlags rewritingFlags = humanSpec.getRewritingFlags();
MachineRewritingFlags.Builder builder = MachineRewritingFlags.builder();
DesugaredLibraryAmender.run(
rewritingFlags.getAmendLibraryMethod(), appInfo, reporter, ComputedApiLevel.unknown());
@@ -102,7 +92,7 @@
.convertRetargetFlags(rewritingFlags, builder, this::warnMissingReferences);
new HumanToMachineEmulatedInterfaceConverter(appInfo)
.convertEmulatedInterfaces(rewritingFlags, appInfo, builder, this::warnMissingReferences);
- new HumanToMachinePrefixConverter(appInfo, builder, synthesizedPrefix, rewritingFlags)
+ new HumanToMachinePrefixConverter(appInfo, builder, humanSpec, rewritingFlags)
.convertPrefixFlags(rewritingFlags, this::warnMissingDexString);
new HumanToMachineWrapperConverter(appInfo)
.convertWrappers(rewritingFlags, builder, this::warnMissingReferences);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java
index 815cb45..9487210 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java
@@ -33,25 +33,29 @@
HumanRewritingFlags rewritingFlags,
MachineRewritingFlags.Builder builder,
BiConsumer<String, Set<? extends DexReference>> warnConsumer) {
- for (DexType wrapperType : rewritingFlags.getWrapperConversions()) {
- DexClass wrapperClass = appInfo.definitionFor(wrapperType);
- if (wrapperClass == null) {
- missingClasses.add(wrapperType);
- continue;
- }
- List<DexMethod> methods;
- if (wrapperClass.isEnum()) {
- methods = ImmutableList.of();
- } else {
- methods = allImplementedMethods(wrapperClass);
- methods.sort(DexMethod::compareTo);
- }
- builder.addWrapper(wrapperType, methods);
- }
+ rewritingFlags
+ .getWrapperConversions()
+ .forEach(
+ (wrapperType, excludedMethods) -> {
+ DexClass wrapperClass = appInfo.definitionFor(wrapperType);
+ if (wrapperClass == null) {
+ missingClasses.add(wrapperType);
+ return;
+ }
+ List<DexMethod> methods;
+ if (wrapperClass.isEnum()) {
+ methods = ImmutableList.of();
+ } else {
+ methods = allImplementedMethods(wrapperClass, excludedMethods);
+ methods.sort(DexMethod::compareTo);
+ }
+ builder.addWrapper(wrapperType, methods);
+ });
warnConsumer.accept("The following types to wrap are missing: ", missingClasses);
}
- private List<DexMethod> allImplementedMethods(DexClass wrapperClass) {
+ private List<DexMethod> allImplementedMethods(
+ DexClass wrapperClass, Set<DexMethod> excludedMethods) {
LinkedList<DexClass> workList = new LinkedList<>();
List<DexMethod> implementedMethods = new ArrayList<>();
workList.add(wrapperClass);
@@ -60,13 +64,15 @@
for (DexEncodedMethod virtualMethod : dexClass.virtualMethods()) {
if (!virtualMethod.isPrivateMethod()) {
assert virtualMethod.isProtectedMethod() || virtualMethod.isPublicMethod();
- boolean alreadyAdded = false;
+ boolean alreadyAdded = excludedMethods.contains(virtualMethod.getReference());
// This looks quadratic but given the size of the collections met in practice for
// desugared libraries (Max ~15) it does not matter.
- for (DexMethod alreadyImplementedMethod : implementedMethods) {
- if (alreadyImplementedMethod.match(virtualMethod.getReference())) {
- alreadyAdded = true;
- break;
+ if (!alreadyAdded) {
+ for (DexMethod alreadyImplementedMethod : implementedMethods) {
+ if (alreadyImplementedMethod.match(virtualMethod.getReference())) {
+ alreadyAdded = true;
+ break;
+ }
}
}
if (!alreadyAdded) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
index 47ef9e3..bd26b562 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
@@ -30,7 +30,6 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.MultiAPILevelLegacyDesugaredLibrarySpecificationParser;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.Timing;
@@ -100,14 +99,6 @@
return humanSpec;
}
- public HumanDesugaredLibrarySpecification convert(
- LegacyDesugaredLibrarySpecification legacySpec, AndroidApp inputApp, InternalOptions options)
- throws IOException {
- DexApplication app =
- AppForSpecConversion.readApp(inputApp, options, legacySpec.isLegacy(), timing);
- return convert(legacySpec, app, options);
- }
-
public HumanDesugaredLibrarySpecification convertForTesting(
LegacyDesugaredLibrarySpecification legacySpec,
Path desugaredJDKLib,
@@ -117,12 +108,11 @@
DexApplication app =
AppForSpecConversion.readAppForTesting(
desugaredJDKLib, androidLib, options, legacySpec.isLibraryCompilation(), timing);
- return convert(legacySpec, app, options);
+ return convert(legacySpec, app);
}
public HumanDesugaredLibrarySpecification convert(
- LegacyDesugaredLibrarySpecification legacySpec, DexApplication app, InternalOptions options)
- throws IOException {
+ LegacyDesugaredLibrarySpecification legacySpec, DexApplication app) throws IOException {
timing.begin("Legacy to Human convert");
LibraryValidator.validate(
app,
@@ -136,7 +126,7 @@
Origin origin = Origin.unknown();
HumanRewritingFlags humanRewritingFlags =
convertRewritingFlags(legacySpec.getRewritingFlags(), app, origin);
- if (options.getMinApiLevel().isLessThanOrEqualTo(LEGACY_HACK_LEVEL)
+ if (app.options.getMinApiLevel().isLessThanOrEqualTo(LEGACY_HACK_LEVEL)
&& legacySpec.isLibraryCompilation()) {
timing.begin("Legacy hacks");
HumanRewritingFlags.Builder builder =
@@ -271,7 +261,15 @@
List<DexClassAndMethod> methodsWithName =
findMethodsWithName(name, dexClass, builder, app);
for (DexClassAndMethod dexClassAndMethod : methodsWithName) {
- builder.retargetMethod(dexClassAndMethod.getReference(), rewrittenType);
+ DexEncodedMethod definition = dexClassAndMethod.getDefinition();
+ if (definition.isStatic()
+ || definition.isFinal()
+ || dexClassAndMethod.getHolder().isFinal()) {
+ builder.retargetMethod(dexClassAndMethod.getReference(), rewrittenType);
+ } else {
+ builder.retargetMethodEmulatedDispatch(
+ dexClassAndMethod.getReference(), rewrittenType);
+ }
}
});
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index 33f7037..da7d679 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -42,6 +42,7 @@
import com.google.common.collect.ImmutableSet;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
@@ -408,11 +409,15 @@
// We introduce forwarding methods only once all desugaring has been performed to avoid
// confusing the look-up with inserted forwarding methods.
- public final void finalizeProcessing(InterfaceProcessingDesugaringEventConsumer eventConsumer) {
+ public void finalizeProcessing(InterfaceProcessingDesugaringEventConsumer eventConsumer) {
newSyntheticMethods.forEach(
(clazz, newForwardingMethods) -> {
- clazz.addVirtualMethods(newForwardingMethods.toDefinitionSet());
- newForwardingMethods.forEach(eventConsumer::acceptForwardingMethod);
+ List<ProgramMethod> sorted = new ArrayList<>(newForwardingMethods.toCollection());
+ sorted.sort(Comparator.comparing(ProgramMethod::getReference));
+ for (ProgramMethod method : sorted) {
+ clazz.addVirtualMethod(method.getDefinition());
+ eventConsumer.acceptForwardingMethod(method);
+ }
});
newExtraInterfaceSignatures.forEach(
(clazz, extraInterfaceSignatures) -> {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
index 4c45086..e305984 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.cf.code.CfNew;
import com.android.tools.r8.cf.code.CfStackInstruction;
import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
-import com.android.tools.r8.cf.code.CfStaticFieldRead;
import com.android.tools.r8.cf.code.CfStore;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppView;
@@ -32,7 +31,6 @@
import com.android.tools.r8.ir.desugar.LocalStackAllocator;
import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.Box;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import java.util.ArrayDeque;
import java.util.Collection;
@@ -121,11 +119,6 @@
eventConsumer.acceptLambdaClass(lambdaClass, context);
- if (lambdaClass.isStateless()) {
- return ImmutableList.of(
- new CfStaticFieldRead(lambdaClass.lambdaField, lambdaClass.lambdaField));
- }
-
DexTypeList captureTypes = lambdaClass.descriptor.captures;
Deque<CfInstruction> replacement = new ArrayDeque<>(3 + captureTypes.size() * 2);
replacement.add(new CfNew(lambdaClass.getType()));
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 cd28d57..204b1a6 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
@@ -1496,6 +1496,12 @@
}
}
+ if (!appView
+ .getOpenClosedInterfacesCollection()
+ .isDefinitelyInstanceOfStaticType(appViewWithLiveness, inValue)) {
+ return RemoveCheckCastInstructionIfTrivialResult.NO_REMOVALS;
+ }
+
// If the in-value is `null` and the cast-type is a float-array type, then trivial check-cast
// elimination may lead to verification errors. See b/123269162.
if (options.canHaveArtCheckCastVerifierBug()) {
@@ -1609,6 +1615,12 @@
}
Value inValue = instanceOf.value();
+ if (!appView
+ .getOpenClosedInterfacesCollection()
+ .isDefinitelyInstanceOfStaticType(appViewWithLiveness, inValue)) {
+ return false;
+ }
+
TypeElement inType = inValue.getType();
TypeElement instanceOfType =
TypeElement.fromDexType(instanceOf.type(), inType.nullability(), appView);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 4ad92d7..9490667 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -206,13 +206,17 @@
}
public boolean satisfiesRequirementsForSimpleInlining(InvokeMethod invoke, ProgramMethod target) {
- // If we are looking for a simple method, only inline if actually simple.
- Code code = target.getDefinition().getCode();
- int instructionLimit =
- inlinerOptions.getSimpleInliningInstructionLimit()
- + getInliningInstructionLimitIncrement(invoke, target);
- if (code.estimatedSizeForInliningAtMost(instructionLimit)) {
- return true;
+ // Code size modified by inlining, so only read for non-concurrent methods.
+ boolean deterministic = !methodProcessor.isProcessedConcurrently(target);
+ if (deterministic) {
+ // If we are looking for a simple method, only inline if actually simple.
+ Code code = target.getDefinition().getCode();
+ int instructionLimit =
+ inlinerOptions.getSimpleInliningInstructionLimit()
+ + getInliningInstructionLimitIncrement(invoke, target);
+ if (code.estimatedSizeForInliningAtMost(instructionLimit)) {
+ return true;
+ }
}
// Even if the inlinee is big it may become simple after inlining. We therefore check if the
// inlinee's simple inlining constraint is satisfied by the invoke.
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 27a4893..2ef8dd2 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
@@ -17,7 +17,6 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.Assume;
import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.CheckCast;
import com.android.tools.r8.ir.code.DominatorTree;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -93,10 +92,10 @@
InvokeVirtual devirtualizedInvoke = devirtualizedCall.get(origin.asInvokeInterface());
// Extract the newly added check-cast instruction, if any.
- CheckCast newCheckCast = null;
+ SafeCheckCast newCheckCast = null;
Value newReceiver = devirtualizedInvoke.getReceiver();
- if (!newReceiver.isPhi() && newReceiver.definition.isCheckCast()) {
- CheckCast definition = newReceiver.definition.asCheckCast();
+ if (!newReceiver.isPhi() && newReceiver.definition.isSafeCheckCast()) {
+ SafeCheckCast definition = newReceiver.definition.asSafeCheckCast();
if (newCheckCastInstructions.contains(definition)) {
newCheckCast = definition;
}
@@ -136,8 +135,7 @@
InvokeVirtual.lookupSingleTarget(
appView,
context,
- invoke.getReceiver().getDynamicUpperBoundType(appView),
- invoke.getReceiver().getDynamicLowerBoundType(appView),
+ invoke.getReceiver().getDynamicType(appView),
invokedMethod);
if (newSingleTarget != null
&& newSingleTarget.getReference() == singleTarget.getReference()) {
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 b4584c4..08edc70 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
@@ -124,7 +124,7 @@
private boolean mayPropagateValueFor(DexClassAndField field) {
if (field.isProgramField()) {
- return appView.appInfo().mayPropagateValueFor(field.getReference());
+ return appView.appInfo().mayPropagateValueFor(appView, field.getReference());
}
return appView.appInfo().assumedValues.containsKey(field.getReference())
|| appView.appInfo().noSideEffects.containsKey(field.getReference());
@@ -132,7 +132,7 @@
private boolean mayPropagateValueFor(DexClassAndMethod method) {
if (method.isProgramMethod()) {
- return appView.appInfo().mayPropagateValueFor(method.getReference());
+ return appView.appInfo().mayPropagateValueFor(appView, method.getReference());
}
return appView.appInfo().assumedValues.containsKey(method.getReference())
|| appView.appInfo().noSideEffects.containsKey(method.getReference());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
index 715dfdd..d758988 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
@@ -40,6 +40,7 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
@@ -448,11 +449,13 @@
}
private void handleInvokeDirect(InvokeDirect invoke) {
- if (!appView.enableWholeProgramOptimizations()) {
+ if (!appView.hasLiveness()) {
killAllNonFinalActiveFields();
return;
}
+ AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+
DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, method);
if (singleTarget == null || !singleTarget.getDefinition().isInstanceInitializer()) {
killAllNonFinalActiveFields();
@@ -470,7 +473,9 @@
fieldInitializationInfos.forEachWithDeterministicOrder(
appView,
(field, info) -> {
- if (!appView.appInfo().withLiveness().mayPropagateValueFor(field.getReference())) {
+ if (!appViewWithLiveness
+ .appInfo()
+ .mayPropagateValueFor(appViewWithLiveness, field.getReference())) {
return;
}
if (info.isArgumentInitializationInfo()) {
@@ -485,7 +490,7 @@
}
} else if (info.isSingleValue()) {
SingleValue value = info.asSingleValue();
- if (value.isMaterializableInContext(appView.withLiveness(), method)) {
+ if (value.isMaterializableInContext(appViewWithLiveness, method)) {
Value object = invoke.getReceiver().getAliasedValue();
FieldAndObject fieldAndObject = new FieldAndObject(field.getReference(), object);
if (field.isFinal()) {
@@ -782,12 +787,13 @@
}
private void applyObjectState(Value value, ObjectState objectState) {
+ AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
objectState.forEachAbstractFieldValue(
(field, fieldValue) -> {
- if (appView.appInfoWithLiveness().mayPropagateValueFor(field)
+ if (appViewWithLiveness.appInfo().mayPropagateValueFor(appViewWithLiveness, field)
&& fieldValue.isSingleValue()) {
SingleValue singleFieldValue = fieldValue.asSingleValue();
- if (singleFieldValue.isMaterializableInContext(appView.withLiveness(), method)) {
+ if (singleFieldValue.isMaterializableInContext(appViewWithLiveness, method)) {
activeState.putFinalInstanceField(
new FieldAndObject(field, value), new MaterializableValue(singleFieldValue));
}
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 e6494f3..c3c21f1 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
@@ -487,11 +487,12 @@
continue;
}
- ClassTypeElement exactReceiverType =
- ClassTypeElement.create(eligibleClass.type, Nullability.definitelyNotNull(), appView);
+ DynamicType exactReceiverType =
+ DynamicType.createExact(
+ ClassTypeElement.create(
+ eligibleClass.getType(), Nullability.definitelyNotNull(), appView));
ProgramMethod singleTarget =
- invoke.lookupSingleProgramTarget(
- appView, method, exactReceiverType, exactReceiverType);
+ invoke.lookupSingleProgramTarget(appView, method, exactReceiverType);
if (singleTarget == null || !indirectMethodCallsOnInstance.contains(singleTarget)) {
throw new IllegalClassInlinerStateException();
}
@@ -1046,12 +1047,12 @@
int parameter = 0;
if (root.isNewInstance()) {
return classInlinerMethodConstraint.isEligibleForNewInstanceClassInlining(
- singleTarget, parameter);
+ appView, eligibleClass, singleTarget, parameter);
}
assert root.isStaticGet();
return classInlinerMethodConstraint.isEligibleForStaticGetClassInlining(
- appView, parameter, objectState, method);
+ appView, eligibleClass, parameter, objectState, method);
}
// Analyzes if a method invoke the eligible instance is passed to is eligible. In short,
@@ -1155,13 +1156,13 @@
singleTarget.getDefinition().getOptimizationInfo().getClassInlinerMethodConstraint();
if (root.isNewInstance()) {
if (!classInlinerMethodConstraint.isEligibleForNewInstanceClassInlining(
- singleTarget, parameter)) {
+ appView, eligibleClass, singleTarget, parameter)) {
return false;
}
} else {
assert root.isStaticGet();
if (!classInlinerMethodConstraint.isEligibleForStaticGetClassInlining(
- appView, parameter, objectState, method)) {
+ appView, eligibleClass, parameter, objectState, method)) {
return false;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsage.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsage.java
index 930edc5..5d7f1e3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsage.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/BottomParameterUsage.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.classinliner.analysis;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
class BottomParameterUsage extends ParameterUsage {
@@ -18,6 +19,11 @@
}
@Override
+ ParameterUsage addCastWithParameter(DexType castType) {
+ return InternalNonEmptyParameterUsage.builder().addCastWithParameter(castType).build();
+ }
+
+ @Override
ParameterUsage addFieldReadFromParameter(DexField field) {
return InternalNonEmptyParameterUsage.builder().addFieldReadFromParameter(field).build();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/InternalNonEmptyParameterUsage.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/InternalNonEmptyParameterUsage.java
index 4bba181..29274b2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/InternalNonEmptyParameterUsage.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/InternalNonEmptyParameterUsage.java
@@ -6,6 +6,7 @@
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.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableMultiset;
@@ -23,6 +24,7 @@
*/
public class InternalNonEmptyParameterUsage extends ParameterUsage {
+ private Set<DexType> castsWithParameter;
private Set<DexField> fieldsReadFromParameter;
private Set<InvokeMethodWithReceiver> methodCallsWithParameterAsReceiver;
@@ -31,16 +33,19 @@
private boolean isParameterUsedAsLock;
InternalNonEmptyParameterUsage(
+ Set<DexType> castsWithParameter,
Set<DexField> fieldsReadFromParameter,
Set<InvokeMethodWithReceiver> methodCallsWithParameterAsReceiver,
boolean isParameterMutated,
boolean isParameterReturned,
boolean isParameterUsedAsLock) {
- assert !fieldsReadFromParameter.isEmpty()
+ assert !castsWithParameter.isEmpty()
+ || !fieldsReadFromParameter.isEmpty()
|| !methodCallsWithParameterAsReceiver.isEmpty()
|| isParameterMutated
|| isParameterReturned
|| isParameterUsedAsLock;
+ this.castsWithParameter = castsWithParameter;
this.fieldsReadFromParameter = fieldsReadFromParameter;
this.methodCallsWithParameterAsReceiver = methodCallsWithParameterAsReceiver;
this.isParameterMutated = isParameterMutated;
@@ -57,11 +62,26 @@
}
@Override
+ ParameterUsage addCastWithParameter(DexType castType) {
+ ImmutableSet.Builder<DexType> newCastsWithParameter = ImmutableSet.builder();
+ newCastsWithParameter.addAll(castsWithParameter);
+ newCastsWithParameter.add(castType);
+ return new InternalNonEmptyParameterUsage(
+ newCastsWithParameter.build(),
+ fieldsReadFromParameter,
+ methodCallsWithParameterAsReceiver,
+ isParameterMutated,
+ isParameterReturned,
+ isParameterUsedAsLock);
+ }
+
+ @Override
InternalNonEmptyParameterUsage addFieldReadFromParameter(DexField field) {
ImmutableSet.Builder<DexField> newFieldsReadFromParameterBuilder = ImmutableSet.builder();
newFieldsReadFromParameterBuilder.addAll(fieldsReadFromParameter);
newFieldsReadFromParameterBuilder.add(field);
return new InternalNonEmptyParameterUsage(
+ castsWithParameter,
newFieldsReadFromParameterBuilder.build(),
methodCallsWithParameterAsReceiver,
isParameterMutated,
@@ -77,6 +97,7 @@
newMethodCallsWithParameterAsReceiverBuilder.addAll(methodCallsWithParameterAsReceiver);
newMethodCallsWithParameterAsReceiverBuilder.add(invoke);
return new InternalNonEmptyParameterUsage(
+ castsWithParameter,
fieldsReadFromParameter,
newMethodCallsWithParameterAsReceiverBuilder.build(),
isParameterMutated,
@@ -96,6 +117,7 @@
methodCallsWithParameterAsReceiver.forEach(
invoke -> methodCallsWithParameterAsReceiverBuilder.add(invoke.getInvokedMethod()));
return new NonEmptyParameterUsage(
+ castsWithParameter,
fieldsReadFromParameter,
methodCallsWithParameterAsReceiverBuilder.build(),
isParameterMutated,
@@ -120,6 +142,7 @@
InternalNonEmptyParameterUsage join(InternalNonEmptyParameterUsage other) {
return builderFromInstance()
+ .addCastsWithParameter(other.castsWithParameter)
.addFieldsReadFromParameter(other.fieldsReadFromParameter)
.addMethodCallsWithParameterAsReceiver(other.methodCallsWithParameterAsReceiver)
.joinIsReceiverMutated(other.isParameterMutated)
@@ -134,6 +157,7 @@
return this;
}
return new InternalNonEmptyParameterUsage(
+ castsWithParameter,
fieldsReadFromParameter,
methodCallsWithParameterAsReceiver,
true,
@@ -147,6 +171,7 @@
return this;
}
return new InternalNonEmptyParameterUsage(
+ castsWithParameter,
fieldsReadFromParameter,
methodCallsWithParameterAsReceiver,
isParameterMutated,
@@ -160,6 +185,7 @@
return this;
}
return new InternalNonEmptyParameterUsage(
+ castsWithParameter,
fieldsReadFromParameter,
methodCallsWithParameterAsReceiver,
isParameterMutated,
@@ -169,6 +195,9 @@
@Override
public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
if (obj == null || obj.getClass() != getClass()) {
return false;
}
@@ -176,6 +205,7 @@
return isParameterMutated == knownParameterUsage.isParameterMutated
&& isParameterReturned == knownParameterUsage.isParameterReturned
&& isParameterUsedAsLock == knownParameterUsage.isParameterUsedAsLock
+ && castsWithParameter.equals(knownParameterUsage.castsWithParameter)
&& fieldsReadFromParameter.equals(knownParameterUsage.fieldsReadFromParameter)
&& methodCallsWithParameterAsReceiver.equals(
knownParameterUsage.methodCallsWithParameterAsReceiver);
@@ -184,9 +214,11 @@
@Override
public int hashCode() {
int hash =
- 31 * (31 + fieldsReadFromParameter.hashCode())
+ 31 * (31 * (31 + castsWithParameter.hashCode()) + fieldsReadFromParameter.hashCode())
+ methodCallsWithParameterAsReceiver.hashCode();
- assert hash == Objects.hash(fieldsReadFromParameter, methodCallsWithParameterAsReceiver);
+ assert hash
+ == Objects.hash(
+ castsWithParameter, fieldsReadFromParameter, methodCallsWithParameterAsReceiver);
hash = (hash << 1) | BooleanUtils.intValue(isParameterMutated);
hash = (hash << 1) | BooleanUtils.intValue(isParameterReturned);
hash = (hash << 1) | BooleanUtils.intValue(isParameterUsedAsLock);
@@ -195,6 +227,7 @@
static class Builder {
+ private ImmutableSet.Builder<DexType> castsWithParameterBuilder;
private ImmutableSet.Builder<DexField> fieldsReadFromParameterBuilder;
private ImmutableSet.Builder<InvokeMethodWithReceiver>
methodCallsWithParameterAsReceiverBuilder;
@@ -203,11 +236,14 @@
private boolean isParameterUsedAsLock;
Builder() {
+ castsWithParameterBuilder = ImmutableSet.builder();
fieldsReadFromParameterBuilder = ImmutableSet.builder();
methodCallsWithParameterAsReceiverBuilder = ImmutableSet.builder();
}
Builder(InternalNonEmptyParameterUsage methodBehavior) {
+ castsWithParameterBuilder =
+ ImmutableSet.<DexType>builder().addAll(methodBehavior.castsWithParameter);
fieldsReadFromParameterBuilder =
ImmutableSet.<DexField>builder().addAll(methodBehavior.fieldsReadFromParameter);
methodCallsWithParameterAsReceiverBuilder =
@@ -218,6 +254,16 @@
isParameterUsedAsLock = methodBehavior.isParameterUsedAsLock;
}
+ Builder addCastWithParameter(DexType castType) {
+ castsWithParameterBuilder.add(castType);
+ return this;
+ }
+
+ Builder addCastsWithParameter(Collection<DexType> castTypes) {
+ castsWithParameterBuilder.addAll(castTypes);
+ return this;
+ }
+
Builder addFieldReadFromParameter(DexField fieldReadFromParameter) {
fieldsReadFromParameterBuilder.add(fieldReadFromParameter);
return this;
@@ -272,6 +318,7 @@
InternalNonEmptyParameterUsage build() {
return new InternalNonEmptyParameterUsage(
+ castsWithParameterBuilder.build(),
fieldsReadFromParameterBuilder.build(),
methodCallsWithParameterAsReceiverBuilder.build(),
isParameterMutated,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsage.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsage.java
index d377305..38056c8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsage.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsage.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.errors.Unreachable;
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.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.Multiset;
@@ -15,6 +16,7 @@
public class NonEmptyParameterUsage extends ParameterUsage {
+ private Set<DexType> castsWithParameter;
private Set<DexField> fieldsReadFromParameter;
private Multiset<DexMethod> methodCallsWithParameterAsReceiver;
@@ -23,16 +25,19 @@
private boolean isParameterUsedAsLock;
NonEmptyParameterUsage(
+ Set<DexType> castsWithParameter,
Set<DexField> fieldsReadFromParameter,
Multiset<DexMethod> methodCallsWithParameterAsReceiver,
boolean isParameterMutated,
boolean isParameterReturned,
boolean isParameterUsedAsLock) {
- assert !fieldsReadFromParameter.isEmpty()
+ assert !castsWithParameter.isEmpty()
+ || !fieldsReadFromParameter.isEmpty()
|| !methodCallsWithParameterAsReceiver.isEmpty()
|| isParameterMutated
|| isParameterReturned
|| isParameterUsedAsLock;
+ this.castsWithParameter = castsWithParameter;
this.fieldsReadFromParameter = fieldsReadFromParameter;
this.methodCallsWithParameterAsReceiver = methodCallsWithParameterAsReceiver;
this.isParameterMutated = isParameterMutated;
@@ -41,6 +46,11 @@
}
@Override
+ ParameterUsage addCastWithParameter(DexType castType) {
+ throw new Unreachable();
+ }
+
+ @Override
ParameterUsage addFieldReadFromParameter(DexField field) {
throw new Unreachable();
}
@@ -64,6 +74,10 @@
return !getFieldsReadFromParameter().isEmpty();
}
+ public Set<DexType> getCastsWithParameter() {
+ return castsWithParameter;
+ }
+
public Set<DexField> getFieldsReadFromParameter() {
return fieldsReadFromParameter;
}
@@ -104,6 +118,9 @@
@Override
public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
if (obj == null || obj.getClass() != getClass()) {
return false;
}
@@ -111,6 +128,7 @@
return isParameterMutated == knownParameterUsage.isParameterMutated
&& isParameterReturned == knownParameterUsage.isParameterReturned
&& isParameterUsedAsLock == knownParameterUsage.isParameterUsedAsLock
+ && castsWithParameter.equals(knownParameterUsage.castsWithParameter)
&& fieldsReadFromParameter.equals(knownParameterUsage.fieldsReadFromParameter)
&& methodCallsWithParameterAsReceiver.equals(
knownParameterUsage.methodCallsWithParameterAsReceiver);
@@ -119,9 +137,11 @@
@Override
public int hashCode() {
int hash =
- 31 * (31 + fieldsReadFromParameter.hashCode())
+ 31 * (31 * (31 + castsWithParameter.hashCode()) + fieldsReadFromParameter.hashCode())
+ methodCallsWithParameterAsReceiver.hashCode();
- assert hash == Objects.hash(fieldsReadFromParameter, methodCallsWithParameterAsReceiver);
+ assert hash
+ == Objects.hash(
+ castsWithParameter, fieldsReadFromParameter, methodCallsWithParameterAsReceiver);
hash = (hash << 1) | BooleanUtils.intValue(isParameterMutated);
hash = (hash << 1) | BooleanUtils.intValue(isParameterReturned);
hash = (hash << 1) | BooleanUtils.intValue(isParameterUsedAsLock);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsage.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsage.java
index e2ad8da..2822d6f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsage.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ParameterUsage.java
@@ -5,10 +5,13 @@
package com.android.tools.r8.ir.optimize.classinliner.analysis;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
public abstract class ParameterUsage {
+ abstract ParameterUsage addCastWithParameter(DexType castType);
+
abstract ParameterUsage addFieldReadFromParameter(DexField field);
abstract ParameterUsage addMethodCallWithParameterAsReceiver(InvokeMethodWithReceiver invoke);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
index 152c212..4d45a08 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
@@ -171,7 +171,11 @@
private ParameterUsages analyzeCheckCast(CheckCast checkCast, NonEmptyParameterUsages state) {
// Mark the value as ineligible for class inlining if it has phi users.
- return checkCast.outValue().hasPhiUsers() ? fail(checkCast, state) : state;
+ if (checkCast.outValue().hasPhiUsers()) {
+ return fail(checkCast, state);
+ }
+ return state.rebuildParameter(
+ checkCast.object(), (context, usage) -> usage.addCastWithParameter(checkCast.getType()));
}
private ParameterUsages analyzeIf(If theIf, NonEmptyParameterUsages state) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsage.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsage.java
index 93577ad..bf49e63 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsage.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/UnknownParameterUsage.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.classinliner.analysis;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
class UnknownParameterUsage extends ParameterUsage {
@@ -18,6 +19,11 @@
}
@Override
+ ParameterUsage addCastWithParameter(DexType castType) {
+ return this;
+ }
+
+ @Override
UnknownParameterUsage addFieldReadFromParameter(DexField field) {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java
index 1826502..f46ae29 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysFalseClassInlinerMethodConstraint.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.classinliner.constraint;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
@@ -34,13 +35,18 @@
}
@Override
- public boolean isEligibleForNewInstanceClassInlining(ProgramMethod method, int parameter) {
+ public boolean isEligibleForNewInstanceClassInlining(
+ AppView<AppInfoWithLiveness> appView,
+ DexProgramClass candidateClass,
+ ProgramMethod method,
+ int parameter) {
return false;
}
@Override
public boolean isEligibleForStaticGetClassInlining(
AppView<AppInfoWithLiveness> appView,
+ DexProgramClass candidateClass,
int parameter,
ObjectState objectState,
ProgramMethod context) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java
index 1f33962..8efec57 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/AlwaysTrueClassInlinerMethodConstraint.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.classinliner.constraint;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
@@ -34,13 +35,18 @@
}
@Override
- public boolean isEligibleForNewInstanceClassInlining(ProgramMethod method, int parameter) {
+ public boolean isEligibleForNewInstanceClassInlining(
+ AppView<AppInfoWithLiveness> appView,
+ DexProgramClass candidateClass,
+ ProgramMethod method,
+ int parameter) {
return true;
}
@Override
public boolean isEligibleForStaticGetClassInlining(
AppView<AppInfoWithLiveness> appView,
+ DexProgramClass candidateClass,
int parameter,
ObjectState objectState,
ProgramMethod context) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java
index 5bc6317..994cdaa 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ClassInlinerMethodConstraint.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.classinliner.constraint;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
@@ -18,10 +19,15 @@
ParameterUsage getParameterUsage(int parameter);
- boolean isEligibleForNewInstanceClassInlining(ProgramMethod method, int parameter);
+ boolean isEligibleForNewInstanceClassInlining(
+ AppView<AppInfoWithLiveness> appView,
+ DexProgramClass candidateClass,
+ ProgramMethod method,
+ int parameter);
boolean isEligibleForStaticGetClassInlining(
AppView<AppInfoWithLiveness> appView,
+ DexProgramClass candidateClass,
int parameter,
ObjectState objectState,
ProgramMethod context);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java
index eaae1fe..bd524a5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java
@@ -8,6 +8,8 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.proto.ArgumentInfo;
import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
@@ -68,15 +70,30 @@
}
@Override
- public boolean isEligibleForNewInstanceClassInlining(ProgramMethod method, int parameter) {
+ public boolean isEligibleForNewInstanceClassInlining(
+ AppView<AppInfoWithLiveness> appView,
+ DexProgramClass candidateClass,
+ ProgramMethod method,
+ int parameter) {
AnalysisContext defaultContext = AnalysisContext.getDefaultContext();
ParameterUsage usage = usages.get(parameter).get(defaultContext);
- return !usage.isTop();
+ if (usage.isBottom()) {
+ return true;
+ }
+ if (usage.isTop()) {
+ return false;
+ }
+ NonEmptyParameterUsage knownUsage = usage.asNonEmpty();
+ if (hasUnsafeCast(appView, candidateClass, knownUsage)) {
+ return false;
+ }
+ return true;
}
@Override
public boolean isEligibleForStaticGetClassInlining(
AppView<AppInfoWithLiveness> appView,
+ DexProgramClass candidateClass,
int parameter,
ObjectState objectState,
ProgramMethod context) {
@@ -100,6 +117,9 @@
// We will not be able to remove the monitor instruction afterwards.
return false;
}
+ if (hasUnsafeCast(appView, candidateClass, knownUsage)) {
+ return false;
+ }
for (DexField fieldReadFromParameter : knownUsage.getFieldsReadFromParameter()) {
DexClass holder = appView.definitionFor(fieldReadFromParameter.getHolderType());
DexEncodedField definition = fieldReadFromParameter.lookupOnClass(holder);
@@ -117,4 +137,20 @@
}
return true;
}
+
+ private boolean hasUnsafeCast(
+ AppView<AppInfoWithLiveness> appView,
+ DexProgramClass candidateClass,
+ NonEmptyParameterUsage knownUsage) {
+ for (DexType castType : knownUsage.getCastsWithParameter()) {
+ if (!castType.isClassType()) {
+ return true;
+ }
+ DexClass castClass = appView.definitionFor(castType);
+ if (castClass == null || !appView.appInfo().isSubtype(candidateClass, castClass)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index 40ccc32..700c1d7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -12,6 +12,7 @@
import static com.android.tools.r8.ir.code.Opcodes.CHECK_CAST;
import static com.android.tools.r8.ir.code.Opcodes.CONST_CLASS;
import static com.android.tools.r8.ir.code.Opcodes.IF;
+import static com.android.tools.r8.ir.code.Opcodes.INIT_CLASS;
import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_GET;
import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_PUT;
import static com.android.tools.r8.ir.code.Opcodes.INVOKE_CUSTOM;
@@ -62,6 +63,7 @@
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.InitClass;
import com.android.tools.r8.ir.code.InstanceGet;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InvokeCustom;
@@ -267,6 +269,9 @@
case CHECK_CAST:
analyzeCheckCast(instruction.asCheckCast(), eligibleEnums);
break;
+ case INIT_CLASS:
+ analyzeInitClass(instruction.asInitClass(), eligibleEnums);
+ break;
case INVOKE_CUSTOM:
analyzeInvokeCustom(instruction.asInvokeCustom(), eligibleEnums, code.context());
break;
@@ -428,6 +433,13 @@
markEnumAsUnboxable(Reason.DOWN_CAST, enumClass);
}
+ private void analyzeInitClass(InitClass initClass, Set<DexType> eligibleEnums) {
+ DexProgramClass enumClass = getEnumUnboxingCandidateOrNull(initClass.getClassValue());
+ if (enumClass != null) {
+ eligibleEnums.add(enumClass.getType());
+ }
+ }
+
private boolean allowCheckCast(CheckCast checkCast) {
TypeElement objectType = checkCast.object().getDynamicUpperBoundType(appView);
return objectType.equalUpToNullability(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 1a62ad5..f223300 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.InitClass;
import com.android.tools.r8.ir.code.InstanceGet;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -139,6 +140,15 @@
continue;
}
+ if (instruction.isInitClass()) {
+ InitClass initClass = instruction.asInitClass();
+ DexType enumType = getEnumTypeOrNull(initClass.getClassValue());
+ if (enumType != null) {
+ iterator.removeOrReplaceByDebugLocalRead();
+ }
+ continue;
+ }
+
if (instruction.isIf()) {
If ifInstruction = instruction.asIf();
if (!ifInstruction.isZeroTest()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
index 65b9aaa..8853607 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -155,7 +155,7 @@
.getFieldAccessInfoCollection()
.get(field.getReference())
.hasReflectiveAccess();
- if (appView.appInfo().mayPropagateValueFor(field.getReference())) {
+ if (appView.appInfo().mayPropagateValueFor(appView, field.getReference())) {
getFieldOptimizationInfoForUpdating(field).setAbstractValue(abstractValue);
}
}
@@ -192,7 +192,7 @@
@Override
public synchronized void methodReturnsAbstractValue(
DexEncodedMethod method, AppView<AppInfoWithLiveness> appView, AbstractValue value) {
- if (appView.appInfo().mayPropagateValueFor(method.getReference())) {
+ if (appView.appInfo().mayPropagateValueFor(appView, method.getReference())) {
getMethodOptimizationInfoForUpdating(method).markReturnsAbstractValue(value);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index a9e3cba..5b65a8a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -63,7 +63,7 @@
@Override
public void recordFieldHasAbstractValue(
DexEncodedField field, AppView<AppInfoWithLiveness> appView, AbstractValue abstractValue) {
- if (appView.appInfo().mayPropagateValueFor(field.getReference())) {
+ if (appView.appInfo().mayPropagateValueFor(appView, field.getReference())) {
field.getMutableOptimizationInfo().setAbstractValue(abstractValue);
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
index f24cac2..8055dc4 100644
--- a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
@@ -52,9 +52,16 @@
return false;
}
// This is a visibility forward, so check for the direct target.
- DexEncodedMethod targetMethod =
- appView.appInfo().unsafeResolveMethodDueToDexFormat(target).getSingleTarget();
- if (targetMethod == null || !targetMethod.accessFlags.isPublic()) {
+ ProgramMethod targetMethod =
+ appView.appInfo().unsafeResolveMethodDueToDexFormat(target).getResolvedProgramMethod();
+ if (targetMethod == null || !targetMethod.getAccessFlags().isPublic()) {
+ return false;
+ }
+ if (definition.isStatic()
+ && method.getHolder().hasClassInitializer()
+ && method
+ .getHolder()
+ .classInitializationMayHaveSideEffectsInContext(appView, targetMethod)) {
return false;
}
if (Log.ENABLED) {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index cca53b7..4599085 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -435,7 +435,7 @@
// OK, this can be rewritten to have void return type.
continue;
}
- if (!appView.appInfo().mayPropagateValueFor(method)) {
+ if (!appView.appInfo().mayPropagateValueFor(appView, method)) {
return null;
}
AbstractValue returnValueForMethod =
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/EmptyOpenClosedInterfacesAnalysis.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/EmptyOpenClosedInterfacesAnalysis.java
new file mode 100644
index 0000000..6e09082
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/EmptyOpenClosedInterfacesAnalysis.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.interfaces.analysis;
+
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.IRCode;
+
+public class EmptyOpenClosedInterfacesAnalysis extends OpenClosedInterfacesAnalysis {
+
+ private static final EmptyOpenClosedInterfacesAnalysis INSTANCE =
+ new EmptyOpenClosedInterfacesAnalysis();
+
+ private EmptyOpenClosedInterfacesAnalysis() {}
+
+ static EmptyOpenClosedInterfacesAnalysis getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public void analyze(ProgramMethod method, IRCode code) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void prepareForPrimaryOptimizationPass() {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void onPrimaryOptimizationPassComplete() {
+ // Intentionally empty.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/OpenClosedInterfacesAnalysis.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/OpenClosedInterfacesAnalysis.java
new file mode 100644
index 0000000..dc13d50
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/OpenClosedInterfacesAnalysis.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.interfaces.analysis;
+
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.IRCode;
+
+public abstract class OpenClosedInterfacesAnalysis {
+
+ public static EmptyOpenClosedInterfacesAnalysis empty() {
+ return EmptyOpenClosedInterfacesAnalysis.getInstance();
+ }
+
+ public abstract void analyze(ProgramMethod method, IRCode code);
+
+ public abstract void prepareForPrimaryOptimizationPass();
+
+ public abstract void onPrimaryOptimizationPassComplete();
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/OpenClosedInterfacesAnalysisImpl.java b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/OpenClosedInterfacesAnalysisImpl.java
new file mode 100644
index 0000000..4e474f4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/analysis/OpenClosedInterfacesAnalysisImpl.java
@@ -0,0 +1,200 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.interfaces.analysis;
+
+import static com.android.tools.r8.ir.code.Opcodes.ARRAY_PUT;
+import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_PUT;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_INTERFACE;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_STATIC;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_SUPER;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
+import static com.android.tools.r8.ir.code.Opcodes.RETURN;
+import static com.android.tools.r8.ir.code.Opcodes.STATIC_PUT;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.InterfaceCollection;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.code.ArrayPut;
+import com.android.tools.r8.ir.code.FieldPut;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Return;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.optimize.interfaces.collection.NonEmptyOpenClosedInterfacesCollection;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions.OpenClosedInterfacesOptions;
+import com.android.tools.r8.utils.SetUtils;
+import com.android.tools.r8.utils.WorkList;
+import com.google.common.collect.Sets;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class OpenClosedInterfacesAnalysisImpl extends OpenClosedInterfacesAnalysis {
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final DexItemFactory dexItemFactory;
+
+ private Set<DexClass> openInterfaces;
+
+ public OpenClosedInterfacesAnalysisImpl(AppView<AppInfoWithLiveness> appView) {
+ this.appView = appView;
+ this.dexItemFactory = appView.dexItemFactory();
+ }
+
+ @Override
+ public void analyze(ProgramMethod method, IRCode code) {
+ if (openInterfaces == null) {
+ return;
+ }
+ // Analyze each instruction that may assign to an interface type.
+ for (Instruction instruction : code.instructions()) {
+ switch (instruction.opcode()) {
+ case ARRAY_PUT:
+ analyzeArrayPut(instruction.asArrayPut());
+ break;
+ case INSTANCE_PUT:
+ case STATIC_PUT:
+ analyzeFieldPut(instruction.asFieldPut());
+ break;
+ case INVOKE_DIRECT:
+ case INVOKE_INTERFACE:
+ case INVOKE_STATIC:
+ case INVOKE_SUPER:
+ case INVOKE_VIRTUAL:
+ analyzeInvokeMethod(instruction.asInvokeMethod());
+ break;
+ case RETURN:
+ analyzeReturn(instruction.asReturn(), method);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private void analyzeArrayPut(ArrayPut arrayPut) {
+ Value array = arrayPut.array();
+ TypeElement arrayType = array.getType();
+ if (!arrayType.isArrayType()) {
+ return;
+ }
+ TypeElement valueType = arrayPut.value().getType();
+ TypeElement arrayMemberType = arrayType.asArrayType().getMemberType();
+ checkAssignment(valueType, arrayMemberType);
+ }
+
+ private void analyzeFieldPut(FieldPut fieldPut) {
+ TypeElement valueType = fieldPut.value().getType();
+ TypeElement fieldType = fieldPut.getField().getTypeElement(appView);
+ checkAssignment(valueType, fieldType);
+ }
+
+ private void analyzeInvokeMethod(InvokeMethod invoke) {
+ DexTypeList parameters = invoke.getInvokedMethod().getParameters();
+ for (int parameterIndex = 0; parameterIndex < parameters.size(); parameterIndex++) {
+ Value argument = invoke.getArgumentForParameter(parameterIndex);
+ TypeElement argumentType = argument.getType();
+ TypeElement parameterType = parameters.get(parameterIndex).toTypeElement(appView);
+ checkAssignment(argumentType, parameterType);
+ }
+ }
+
+ private void analyzeReturn(Return returnInstruction, ProgramMethod context) {
+ if (returnInstruction.isReturnVoid()) {
+ return;
+ }
+ TypeElement valueType = returnInstruction.returnValue().getType();
+ TypeElement returnType = context.getReturnType().toTypeElement(appView);
+ checkAssignment(valueType, returnType);
+ }
+
+ private void checkAssignment(TypeElement fromType, TypeElement toType) {
+ // If the type is an interface type, then check that the assigned value is a subtype of the
+ // interface type, or mark the interface as open.
+ if (!toType.isClassType()) {
+ return;
+ }
+ ClassTypeElement toClassType = toType.asClassType();
+ if (toClassType.getClassType() != dexItemFactory.objectType) {
+ return;
+ }
+ InterfaceCollection interfaceCollection = toClassType.getInterfaces();
+ interfaceCollection.forEachKnownInterface(
+ knownInterfaceType -> {
+ DexClass knownInterface = appView.definitionFor(knownInterfaceType);
+ if (knownInterface == null) {
+ return;
+ }
+ assert knownInterface.isInterface();
+ if (fromType.lessThanOrEqualUpToNullability(toType, appView)) {
+ return;
+ }
+ assert verifyOpenInterfaceWitnessIsSuppressed(fromType, knownInterface);
+ openInterfaces.add(knownInterface);
+ });
+ }
+
+ @Override
+ public void prepareForPrimaryOptimizationPass() {
+ openInterfaces = Sets.newConcurrentHashSet();
+ }
+
+ @Override
+ public void onPrimaryOptimizationPassComplete() {
+ // If open interfaces are not allowed and there are one or more suppressions, we should find at
+ // least one open interface.
+ OpenClosedInterfacesOptions options = appView.options().getOpenClosedInterfacesOptions();
+ assert options.isOpenInterfacesAllowed()
+ || !options.hasSuppressions()
+ || !openInterfaces.isEmpty()
+ : "Expected to find at least one open interface";
+
+ includeParentOpenInterfaces();
+ appView.setOpenClosedInterfacesCollection(
+ new NonEmptyOpenClosedInterfacesCollection(
+ openInterfaces.stream()
+ .map(DexClass::getType)
+ .collect(
+ Collectors.toCollection(
+ () -> SetUtils.newIdentityHashSet(openInterfaces.size())))));
+ openInterfaces = null;
+ }
+
+ private void includeParentOpenInterfaces() {
+ // This includes all parent interfaces of each open interface in the set of open interfaces,
+ // by using the open interfaces as the seen set.
+ WorkList<DexClass> worklist = WorkList.newWorkList(openInterfaces);
+ worklist.addAllIgnoringSeenSet(openInterfaces);
+ while (worklist.hasNext()) {
+ DexClass openInterface = worklist.next();
+ for (DexType indirectOpenInterfaceType : openInterface.getInterfaces()) {
+ DexClass indirectOpenInterfaceDefinition = appView.definitionFor(indirectOpenInterfaceType);
+ if (indirectOpenInterfaceDefinition != null) {
+ worklist.addIfNotSeen(indirectOpenInterfaceDefinition);
+ }
+ }
+ }
+ }
+
+ private boolean verifyOpenInterfaceWitnessIsSuppressed(
+ TypeElement valueType, DexClass openInterface) {
+ OpenClosedInterfacesOptions options = appView.options().getOpenClosedInterfacesOptions();
+ assert options.isSuppressed(appView, valueType, openInterface)
+ : "Unexpected open interface "
+ + openInterface.getTypeName()
+ + " (assignment: "
+ + valueType
+ + ")";
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/collection/DefaultOpenClosedInterfacesCollection.java b/src/main/java/com/android/tools/r8/optimize/interfaces/collection/DefaultOpenClosedInterfacesCollection.java
new file mode 100644
index 0000000..df264e4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/collection/DefaultOpenClosedInterfacesCollection.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.interfaces.collection;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
+
+/** Default oracle for that answers "maybe open" for each interface. */
+public class DefaultOpenClosedInterfacesCollection extends OpenClosedInterfacesCollection {
+
+ private static final DefaultOpenClosedInterfacesCollection INSTANCE =
+ new DefaultOpenClosedInterfacesCollection();
+
+ private DefaultOpenClosedInterfacesCollection() {}
+
+ static DefaultOpenClosedInterfacesCollection getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public boolean isDefinitelyClosed(DexClass clazz) {
+ return false;
+ }
+
+ @Override
+ public OpenClosedInterfacesCollection rewrittenWithLens(GraphLens graphLens) {
+ return this;
+ }
+
+ @Override
+ public OpenClosedInterfacesCollection withoutPrunedItems(PrunedItems prunedItems) {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/collection/NonEmptyOpenClosedInterfacesCollection.java b/src/main/java/com/android/tools/r8/optimize/interfaces/collection/NonEmptyOpenClosedInterfacesCollection.java
new file mode 100644
index 0000000..b3382e0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/collection/NonEmptyOpenClosedInterfacesCollection.java
@@ -0,0 +1,51 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.interfaces.collection;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.utils.SetUtils;
+import java.util.Set;
+
+public class NonEmptyOpenClosedInterfacesCollection extends OpenClosedInterfacesCollection {
+
+ private final Set<DexType> openInterfaceTypes;
+
+ public NonEmptyOpenClosedInterfacesCollection(Set<DexType> openInterfaceTypes) {
+ this.openInterfaceTypes = openInterfaceTypes;
+ }
+
+ @Override
+ public boolean isDefinitelyClosed(DexClass clazz) {
+ assert clazz.isInterface();
+ return !openInterfaceTypes.contains(clazz.getType());
+ }
+
+ @Override
+ public OpenClosedInterfacesCollection rewrittenWithLens(GraphLens graphLens) {
+ Set<DexType> rewrittenOpenInterfaceTypes =
+ SetUtils.newIdentityHashSet(openInterfaceTypes.size());
+ for (DexType openInterfaceType : openInterfaceTypes) {
+ rewrittenOpenInterfaceTypes.add(graphLens.lookupType(openInterfaceType));
+ }
+ return new NonEmptyOpenClosedInterfacesCollection(rewrittenOpenInterfaceTypes);
+ }
+
+ @Override
+ public OpenClosedInterfacesCollection withoutPrunedItems(PrunedItems prunedItems) {
+ if (!prunedItems.hasRemovedClasses()) {
+ return this;
+ }
+ Set<DexType> prunedOpenInterfaceTypes = SetUtils.newIdentityHashSet(openInterfaceTypes.size());
+ for (DexType openInterfaceType : openInterfaceTypes) {
+ if (!prunedItems.isRemoved(openInterfaceType)) {
+ prunedOpenInterfaceTypes.add(openInterfaceType);
+ }
+ }
+ return new NonEmptyOpenClosedInterfacesCollection(prunedOpenInterfaceTypes);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/interfaces/collection/OpenClosedInterfacesCollection.java b/src/main/java/com/android/tools/r8/optimize/interfaces/collection/OpenClosedInterfacesCollection.java
new file mode 100644
index 0000000..5b009ba
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/interfaces/collection/OpenClosedInterfacesCollection.java
@@ -0,0 +1,96 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.interfaces.collection;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.function.Supplier;
+
+/**
+ * Knowledge about open/closed interfaces.
+ *
+ * <p>An interface type is "open" if it may store an instance that is not a subtype of the given
+ * interface.
+ *
+ * <p>An interface type is "closed" if it is guaranteed to store instances that are subtypes of the
+ * given interface.
+ */
+public abstract class OpenClosedInterfacesCollection {
+
+ public static DefaultOpenClosedInterfacesCollection getDefault() {
+ return DefaultOpenClosedInterfacesCollection.getInstance();
+ }
+
+ public abstract boolean isDefinitelyClosed(DexClass clazz);
+
+ public final boolean isMaybeOpen(DexClass clazz) {
+ return !isDefinitelyClosed(clazz);
+ }
+
+ public final boolean isDefinitelyInstanceOfStaticType(
+ AppView<AppInfoWithLiveness> appView, Value value) {
+ return isDefinitelyInstanceOfStaticType(
+ appView, () -> value.getDynamicType(appView), value.getType());
+ }
+
+ public final boolean isDefinitelyInstanceOfStaticType(
+ AppView<?> appView, Supplier<DynamicType> dynamicTypeSupplier, TypeElement staticType) {
+ if (!staticType.isClassType()) {
+ // Only interface types may store instances that are not a subtype of the static type.
+ return true;
+ }
+ ClassTypeElement staticClassType = staticType.asClassType();
+ if (staticClassType.getClassType() != appView.dexItemFactory().objectType) {
+ // Ditto.
+ return true;
+ }
+ if (staticClassType.nullability().isDefinitelyNull()) {
+ // The null value is definitely an instance of the static type.
+ return true;
+ }
+ boolean isStaticTypeDefinitelyClosed =
+ staticClassType
+ .getInterfaces()
+ .allKnownInterfacesMatch(
+ knownInterfaceType -> {
+ DexClass knownInterface = appView.definitionFor(knownInterfaceType);
+ return knownInterface != null && isDefinitelyClosed(knownInterface);
+ });
+ if (isStaticTypeDefinitelyClosed) {
+ return true;
+ }
+ DynamicType dynamicType = dynamicTypeSupplier.get();
+ if (dynamicType.isNullType()) {
+ return true;
+ }
+ if (dynamicType.isUnknown()) {
+ return false;
+ }
+ TypeElement dynamicUpperBoundType = dynamicType.getDynamicUpperBoundType(staticType);
+ if (!dynamicUpperBoundType.isClassType()) {
+ // Should not happen, since the dynamic type should be assignable to the static type.
+ assert false;
+ return false;
+ }
+ ClassTypeElement dynamicUpperBoundClassType = dynamicUpperBoundType.asClassType();
+ if (dynamicUpperBoundClassType.getClassType() != appView.dexItemFactory().objectType) {
+ // The dynamic upper bound type is a non-interface type. Check if this non-interface type is a
+ // subtype of the static interface type.
+ return dynamicUpperBoundClassType.lessThanOrEqualUpToNullability(staticType, appView);
+ }
+ return false;
+ }
+
+ public abstract OpenClosedInterfacesCollection rewrittenWithLens(GraphLens graphLens);
+
+ public abstract OpenClosedInterfacesCollection withoutPrunedItems(PrunedItems prunedItems);
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/MappingPartition.java b/src/main/java/com/android/tools/r8/retrace/MappingPartition.java
new file mode 100644
index 0000000..a925cc3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/MappingPartition.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import com.android.tools.r8.Keep;
+
+@Keep
+public interface MappingPartition {
+
+ String getKey();
+
+ byte[] getPayload();
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/MappingPartitionKeyInfo.java b/src/main/java/com/android/tools/r8/retrace/MappingPartitionKeyInfo.java
new file mode 100644
index 0000000..56f20b1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/MappingPartitionKeyInfo.java
@@ -0,0 +1,31 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.TypeReference;
+import java.util.function.Consumer;
+
+@Keep
+public interface MappingPartitionKeyInfo {
+
+ void getKeysForClass(ClassReference reference, Consumer<String> keyConsumer);
+
+ void getKeysForClassAndMethodName(
+ ClassReference reference, String methodName, Consumer<String> keyConsumer);
+
+ void getKeysForMethod(MethodReference reference, Consumer<String> keyConsumer);
+
+ void getKeysForField(FieldReference fieldReference, Consumer<String> keyConsumer);
+
+ void getKeysForType(TypeReference typeReference, Consumer<String> keyConsumer);
+
+ static MappingPartitionKeyInfo getDefault(byte[] metadata) {
+ return null;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/MappingPartitioner.java b/src/main/java/com/android/tools/r8/retrace/MappingPartitioner.java
new file mode 100644
index 0000000..10c641a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/MappingPartitioner.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import com.android.tools.r8.Keep;
+
+@Keep
+public interface MappingPartitioner {
+
+ MappingPartitions partition(ProguardMapProducer mapProducer);
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/MappingPartitions.java b/src/main/java/com/android/tools/r8/retrace/MappingPartitions.java
new file mode 100644
index 0000000..9bb9eb7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/MappingPartitions.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace;
+
+import com.android.tools.r8.Keep;
+import java.util.function.Consumer;
+
+@Keep
+public interface MappingPartitions {
+
+ byte[] getMetadata();
+
+ void visitPartitions(Consumer<MappingPartition> consumer);
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/ProguardMapProducer.java b/src/main/java/com/android/tools/r8/retrace/ProguardMapProducer.java
index 81b7f30..c3f94be 100644
--- a/src/main/java/com/android/tools/r8/retrace/ProguardMapProducer.java
+++ b/src/main/java/com/android/tools/r8/retrace/ProguardMapProducer.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.retrace;
import com.android.tools.r8.Keep;
+import com.google.common.primitives.Bytes;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
@@ -25,4 +26,8 @@
static ProguardMapProducer fromPath(Path path) {
return () -> Files.newBufferedReader(path, StandardCharsets.UTF_8);
}
+
+ static ProguardMapProducer fromBytes(byte[]... partitions) {
+ return fromString(new String(Bytes.concat(partitions), StandardCharsets.UTF_8));
+ }
}
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 7464171..d601873 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -48,6 +48,9 @@
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter;
@@ -1035,28 +1038,33 @@
&& !keepInfo.getMethodInfo(method).isPinned(options());
}
- public boolean mayPropagateValueFor(DexClassAndMember<?, ?> member) {
+ public boolean mayPropagateValueFor(
+ AppView<AppInfoWithLiveness> appView, DexClassAndMember<?, ?> member) {
assert checkIfObsolete();
- return member.getReference().apply(this::mayPropagateValueFor, this::mayPropagateValueFor);
+ return member
+ .getReference()
+ .apply(
+ field -> mayPropagateValueFor(appView, field),
+ method -> mayPropagateValueFor(appView, method));
}
- public boolean mayPropagateValueFor(DexField field) {
+ public boolean mayPropagateValueFor(AppView<AppInfoWithLiveness> appView, DexField field) {
assert checkIfObsolete();
if (neverPropagateValue.contains(field)) {
return false;
}
- if (isPinned(field) && !field.getType().isAlwaysNull(this)) {
+ if (isPinned(field) && !field.getType().isAlwaysNull(appView)) {
return false;
}
return true;
}
- public boolean mayPropagateValueFor(DexMethod method) {
+ public boolean mayPropagateValueFor(AppView<AppInfoWithLiveness> appView, DexMethod method) {
assert checkIfObsolete();
if (neverPropagateValue.contains(method)) {
return false;
}
- if (!method.getReturnType().isAlwaysNull(this)
+ if (!method.getReturnType().isAlwaysNull(appView)
&& !getKeepInfo().getMethodInfo(method, this).isOptimizationAllowed(options())) {
return false;
}
@@ -1285,6 +1293,7 @@
}
public DexEncodedMethod lookupSingleTarget(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
Type type,
DexMethod target,
ProgramMethod context,
@@ -1296,9 +1305,9 @@
}
switch (type) {
case VIRTUAL:
- return lookupSingleVirtualTarget(target, context, false, modeledPredicate);
+ return lookupSingleVirtualTarget(appView, target, context, false, modeledPredicate);
case INTERFACE:
- return lookupSingleVirtualTarget(target, context, true, modeledPredicate);
+ return lookupSingleVirtualTarget(appView, target, context, true, modeledPredicate);
case DIRECT:
return lookupDirectTarget(target, context);
case STATIC:
@@ -1311,56 +1320,67 @@
}
public ProgramMethod lookupSingleProgramTarget(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
Type type,
DexMethod target,
ProgramMethod context,
LibraryModeledPredicate modeledPredicate) {
- return asProgramMethodOrNull(lookupSingleTarget(type, target, context, modeledPredicate), this);
+ return asProgramMethodOrNull(
+ lookupSingleTarget(appView, type, target, context, modeledPredicate), this);
}
/** For mapping invoke virtual instruction to single target method. */
public DexEncodedMethod lookupSingleVirtualTarget(
- DexMethod method, ProgramMethod context, boolean isInterface) {
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ DexMethod method,
+ ProgramMethod context,
+ boolean isInterface) {
assert checkIfObsolete();
- return lookupSingleVirtualTarget(
- method, context, isInterface, type -> false, method.holder, null);
+ return lookupSingleVirtualTarget(appView, method, context, isInterface, type -> false);
}
/** For mapping invoke virtual instruction to single target method. */
public DexEncodedMethod lookupSingleVirtualTarget(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
DexMethod method,
ProgramMethod context,
boolean isInterface,
LibraryModeledPredicate modeledPredicate) {
assert checkIfObsolete();
return lookupSingleVirtualTarget(
- method, context, isInterface, modeledPredicate, method.holder, null);
+ appView, method, context, isInterface, modeledPredicate, DynamicType.unknown());
}
public DexEncodedMethod lookupSingleVirtualTarget(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
DexMethod method,
ProgramMethod context,
boolean isInterface,
LibraryModeledPredicate modeledPredicate,
- DexType refinedReceiverType,
- ClassTypeElement receiverLowerBoundType) {
+ DynamicType dynamicReceiverType) {
assert checkIfObsolete();
- assert refinedReceiverType != null;
- if (!refinedReceiverType.isClassType()) {
- // The refined receiver is not of class type and we will not be able to find a single target
- // (it is either primitive or array).
+ assert dynamicReceiverType != null;
+ if (method.getHolderType().isArrayType()) {
+ return null;
+ }
+ TypeElement staticReceiverType = method.getHolderType().toTypeElement(appView);
+ if (!appView
+ .getOpenClosedInterfacesCollection()
+ .isDefinitelyInstanceOfStaticType(appView, () -> dynamicReceiverType, staticReceiverType)) {
return null;
}
DexClass initialResolutionHolder = definitionFor(method.holder);
if (initialResolutionHolder == null || initialResolutionHolder.isInterface() != isInterface) {
return null;
}
+ DexType refinedReceiverType =
+ TypeAnalysis.toRefinedReceiverType(dynamicReceiverType, method, appView);
DexClass refinedReceiverClass = definitionFor(refinedReceiverType);
if (refinedReceiverClass == null) {
// The refined receiver is not defined in the program and we cannot determine the target.
return null;
}
- if (receiverLowerBoundType == null
+ if (!dynamicReceiverType.hasDynamicLowerBoundType()
&& singleTargetLookupCache.hasCachedItem(refinedReceiverType, method)) {
DexEncodedMethod cachedItem =
singleTargetLookupCache.getCachedItem(refinedReceiverType, method);
@@ -1383,7 +1403,10 @@
}
DexEncodedMethod exactTarget =
getMethodTargetFromExactRuntimeInformation(
- refinedReceiverType, receiverLowerBoundType, resolution, refinedReceiverClass);
+ refinedReceiverType,
+ dynamicReceiverType.getDynamicLowerBoundType(),
+ resolution,
+ refinedReceiverClass);
if (exactTarget != null) {
// We are not caching single targets here because the cache does not include the
// lower bound dimension.
@@ -1405,8 +1428,9 @@
}
DexEncodedMethod singleMethodTarget = null;
DexProgramClass refinedLowerBound = null;
- if (receiverLowerBoundType != null) {
- DexClass refinedLowerBoundClass = definitionFor(receiverLowerBoundType.getClassType());
+ if (dynamicReceiverType.hasDynamicLowerBoundType()) {
+ DexClass refinedLowerBoundClass =
+ definitionFor(dynamicReceiverType.getDynamicLowerBoundType().getClassType());
if (refinedLowerBoundClass != null) {
refinedLowerBound = refinedLowerBoundClass.asProgramClass();
// TODO(b/154822960): Check if the lower bound is a subtype of the upper bound.
@@ -1426,7 +1450,7 @@
singleMethodTarget = singleTarget.asMethodTarget().getDefinition();
}
}
- if (receiverLowerBoundType == null) {
+ if (!dynamicReceiverType.hasDynamicLowerBoundType()) {
singleTargetLookupCache.addToCache(refinedReceiverType, method, singleMethodTarget);
}
return singleMethodTarget;
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 4ed94e5..c4af05f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -5,6 +5,7 @@
import static com.android.tools.r8.utils.DescriptorUtils.javaTypeToDescriptor;
+import com.android.tools.r8.InputDependencyGraphConsumer;
import com.android.tools.r8.Version;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.DexField;
@@ -50,6 +51,7 @@
private final DexItemFactory dexItemFactory;
private final Reporter reporter;
+ private final InputDependencyGraphConsumer inputDependencyConsumer;
private final boolean allowTestOptions;
public static final String FLATTEN_PACKAGE_HIERARCHY = "flattenpackagehierarchy";
@@ -120,18 +122,39 @@
public ProguardConfigurationParser(
DexItemFactory dexItemFactory, Reporter reporter) {
- this(dexItemFactory, reporter, false);
+ this(dexItemFactory, reporter, null, false);
}
public ProguardConfigurationParser(
- DexItemFactory dexItemFactory, Reporter reporter, boolean allowTestOptions) {
+ DexItemFactory dexItemFactory,
+ Reporter reporter,
+ InputDependencyGraphConsumer inputDependencyConsumer,
+ boolean allowTestOptions) {
this.dexItemFactory = dexItemFactory;
configurationBuilder = ProguardConfiguration.builder(dexItemFactory, reporter);
this.reporter = reporter;
+ this.inputDependencyConsumer =
+ inputDependencyConsumer != null
+ ? inputDependencyConsumer
+ : emptyInputDependencyGraphConsumer();
this.allowTestOptions = allowTestOptions;
}
+ private static InputDependencyGraphConsumer emptyInputDependencyGraphConsumer() {
+ return new InputDependencyGraphConsumer() {
+ @Override
+ public void accept(Origin dependent, Path dependency) {
+ // ignored.
+ }
+
+ @Override
+ public void finished() {
+ // ignored.
+ }
+ };
+ }
+
public ProguardConfiguration.Builder getConfigurationBuilder() {
return configurationBuilder;
}
@@ -370,7 +393,8 @@
configurationBuilder.setPrintMappingFile(parseFileName(false));
}
} else if (acceptString("applymapping")) {
- configurationBuilder.setApplyMappingFile(parseFileName(false));
+ configurationBuilder.setApplyMappingFile(
+ parseFileInputDependency(inputDependencyConsumer::acceptProguardApplyMapping));
} else if (acceptString("assumenosideeffects")) {
ProguardAssumeNoSideEffectRule rule = parseAssumeNoSideEffectsRule(optionStart);
configurationBuilder.addRule(rule);
@@ -388,9 +412,11 @@
skipWhitespace();
baseDirectory = parseFileName(false);
} else if (acceptString("injars")) {
- configurationBuilder.addInjars(parseClassPath());
+ configurationBuilder.addInjars(
+ parseClassPath(inputDependencyConsumer::acceptProguardInJars));
} else if (acceptString("libraryjars")) {
- configurationBuilder.addLibraryJars(parseClassPath());
+ configurationBuilder.addLibraryJars(
+ parseClassPath(inputDependencyConsumer::acceptProguardLibraryJars));
} else if (acceptString("printseeds")) {
configurationBuilder.setPrintSeeds(true);
skipWhitespace();
@@ -398,11 +424,16 @@
configurationBuilder.setSeedFile(parseFileName(false));
}
} else if (acceptString("obfuscationdictionary")) {
- configurationBuilder.setObfuscationDictionary(parseFileName(false));
+ configurationBuilder.setObfuscationDictionary(
+ parseFileInputDependency(inputDependencyConsumer::acceptProguardObfuscationDictionary));
} else if (acceptString("classobfuscationdictionary")) {
- configurationBuilder.setClassObfuscationDictionary(parseFileName(false));
+ configurationBuilder.setClassObfuscationDictionary(
+ parseFileInputDependency(
+ inputDependencyConsumer::acceptProguardClassObfuscationDictionary));
} else if (acceptString("packageobfuscationdictionary")) {
- configurationBuilder.setPackageObfuscationDictionary(parseFileName(false));
+ configurationBuilder.setPackageObfuscationDictionary(
+ parseFileInputDependency(
+ inputDependencyConsumer::acceptProguardPackageObfuscationDictionary));
} else if (acceptString("alwaysinline")) {
InlineRule rule = parseInlineRule(InlineRule.Type.ALWAYS, optionStart);
configurationBuilder.addRule(rule);
@@ -635,7 +666,7 @@
private void parseInclude() throws ProguardRuleParserException {
TextPosition start = getPosition();
- Path included = parseFileName(false);
+ Path included = parseFileInputDependency(inputDependencyConsumer::acceptProguardInclude);
try {
new ProguardConfigurationSourceParser(new ProguardConfigurationSourceFile(included))
.parse();
@@ -1536,6 +1567,13 @@
return result.toString();
}
+ private Path parseFileInputDependency(BiConsumer<Origin, Path> dependencyConsumer)
+ throws ProguardRuleParserException {
+ Path file = parseFileName(false);
+ dependencyConsumer.accept(origin, file);
+ return file;
+ }
+
private Path parseFileName(boolean stopAfterPathSeparator) throws ProguardRuleParserException {
TextPosition start = getPosition();
skipWhitespace();
@@ -1566,15 +1604,18 @@
return baseDirectory.resolve(fileName);
}
- private List<FilteredClassPath> parseClassPath() throws ProguardRuleParserException {
+ private List<FilteredClassPath> parseClassPath(BiConsumer<Origin, Path> dependencyCallback)
+ throws ProguardRuleParserException {
List<FilteredClassPath> classPath = new ArrayList<>();
skipWhitespace();
TextPosition position = getPosition();
Path file = parseFileName(true);
+ dependencyCallback.accept(origin, file);
ImmutableList<String> filters = parseClassPathFilters();
classPath.add(new FilteredClassPath(file, filters, origin, position));
while (acceptChar(File.pathSeparatorChar)) {
file = parseFileName(true);
+ dependencyCallback.accept(origin, file);
filters = parseClassPathFilters();
classPath.add(new FilteredClassPath(file, filters, origin, position));
}
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 5cc5876..533e24f 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -575,10 +575,7 @@
});
groupsPerPrefix.forEach(
(externalSyntheticTypePrefix, groups) -> {
- // Sort the equivalence groups that go into 'context' including the context type of the
- // representative which is equal to 'context' here (see assert below).
- Comparator<EquivalenceGroup<T>> comparator =
- (a, b) -> a.compareToIncludingContext(b, appView.graphLens(), classToFeatureSplitMap);
+ Comparator<EquivalenceGroup<T>> comparator = this::compareForFinalGroupSorting;
ListUtils.destructiveSort(groups, comparator);
for (int i = 0; i < groups.size(); i++) {
EquivalenceGroup<T> group = groups.get(i);
@@ -588,9 +585,7 @@
.equals(externalSyntheticTypePrefix);
// Two equivalence groups in same context type must be distinct otherwise the assignment
// of the synthetic name will be non-deterministic between the two.
- assert i == 0
- || checkGroupsAreDistinct(
- groups.get(i - 1), group, appView.graphLens(), classToFeatureSplitMap);
+ assert i == 0 || checkGroupsAreDistinct(groups.get(i - 1), group, comparator);
SyntheticKind kind = group.getRepresentative().getKind();
DexType representativeType =
intermediate
@@ -617,6 +612,17 @@
return equivalences;
}
+ private <T extends SyntheticDefinition<?, T, ?>> int compareForFinalGroupSorting(
+ EquivalenceGroup<T> a, EquivalenceGroup<T> b) {
+ // Sort the equivalence groups based on the representative types. The representatives are
+ // deterministically chosen and the internal synthetics deterministically named so using
+ // the internal type as the order is deterministic.
+ return a.getRepresentative()
+ .getHolder()
+ .getType()
+ .compareTo(b.getRepresentative().getHolder().getType());
+ }
+
private static <T extends SyntheticDefinition<?, T, ?>> List<EquivalenceGroup<T>> groupEquivalent(
AppView<?> appView,
List<T> potentialEquivalence,
@@ -695,13 +701,11 @@
}
private static <T extends SyntheticDefinition<?, T, ?>> boolean checkGroupsAreDistinct(
- EquivalenceGroup<T> g1,
- EquivalenceGroup<T> g2,
- GraphLens graphLens,
- ClassToFeatureSplitMap classToFeatureSplitMap) {
- int order = g1.compareToIncludingContext(g2, graphLens, classToFeatureSplitMap);
- assert order != 0;
- assert order != g2.compareToIncludingContext(g1, graphLens, classToFeatureSplitMap);
+ EquivalenceGroup<T> g1, EquivalenceGroup<T> g2, Comparator<EquivalenceGroup<T>> comparator) {
+ int smaller = comparator.compare(g1, g2);
+ assert smaller < 0;
+ int bigger = comparator.compare(g2, g1);
+ assert bigger > 0;
return true;
}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index 959238e..3b7706b 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -21,7 +21,6 @@
import com.android.tools.r8.DexFilePerClassFileConsumer;
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.DirectoryClassFileProvider;
-import com.android.tools.r8.DumpOptions;
import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.ProgramResource;
@@ -31,6 +30,7 @@
import com.android.tools.r8.ResourceException;
import com.android.tools.r8.StringResource;
import com.android.tools.r8.Version;
+import com.android.tools.r8.dump.DumpOptions;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.errors.Unreachable;
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 2b8d72a..672e322 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.DesugarGraphConsumer;
import com.android.tools.r8.DexFilePerClassFileConsumer;
import com.android.tools.r8.DexIndexedConsumer;
-import com.android.tools.r8.DumpOptions;
import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.MapIdProvider;
import com.android.tools.r8.ProgramConsumer;
@@ -23,6 +22,7 @@
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.dex.Marker.Backend;
import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.dump.DumpOptions;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.ExperimentalClassFileVersionDiagnostic;
import com.android.tools.r8.errors.IncompleteNestNestDesugarDiagnosic;
@@ -34,7 +34,9 @@
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.experimental.startup.StartupConfiguration;
import com.android.tools.r8.features.FeatureSplitConfiguration;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -51,6 +53,7 @@
import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.horizontalclassmerging.Policy;
import com.android.tools.r8.inspector.internal.InspectorImpl;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.desugar.TypeRewriter;
import com.android.tools.r8.ir.desugar.TypeRewriter.MachineDesugarPrefixRewritingMapper;
@@ -87,7 +90,6 @@
import com.google.common.collect.Sets;
import java.io.IOException;
import java.io.PrintStream;
-import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -176,7 +178,7 @@
enableMinification = false;
}
- // Constructor for D8.
+ // Constructor for D8, L8, Lint and other non-shrinkers.
public InternalOptions(DexItemFactory factory, Reporter reporter) {
assert reporter != null;
assert factory != null;
@@ -329,8 +331,6 @@
public final OutlineOptions outline = new OutlineOptions();
public boolean enableInitializedClassesInInstanceMethodsAnalysis = true;
public boolean enableRedundantFieldLoadElimination = true;
- // Currently disabled, see b/146957343.
- public boolean enableUninstantiatedTypeOptimizationForInterfaces = false;
// TODO(b/138917494): Disable until we have numbers on potential performance penalties.
public boolean enableRedundantConstNumberOptimization = false;
@@ -734,6 +734,8 @@
private final InlinerOptions inlinerOptions = new InlinerOptions();
private final HorizontalClassMergerOptions horizontalClassMergerOptions =
new HorizontalClassMergerOptions();
+ private final OpenClosedInterfacesOptions openClosedInterfacesOptions =
+ new OpenClosedInterfacesOptions();
private final ProtoShrinkingOptions protoShrinking = new ProtoShrinkingOptions();
private final KotlinOptimizationOptions kotlinOptimizationOptions =
new KotlinOptimizationOptions();
@@ -786,6 +788,10 @@
return desugarSpecificOptions;
}
+ public OpenClosedInterfacesOptions getOpenClosedInterfacesOptions() {
+ return openClosedInterfacesOptions;
+ }
+
private static Set<String> getExtensiveLoggingFilter() {
String property = System.getProperty("com.android.tools.r8.extensiveLoggingFilter");
if (property != null) {
@@ -890,37 +896,27 @@
// If non-null, configuration must be passed to the consumer.
public StringConsumer configurationConsumer = null;
- public void setDesugaredLibrarySpecification(
- DesugaredLibrarySpecification specification, AndroidApp app) {
+ public void setDesugaredLibrarySpecification(DesugaredLibrarySpecification specification) {
if (specification.isEmpty()) {
return;
}
- try {
- // TODO(b/221224178): Move the timing to top level timing in D8, R8 and L8.
- Timing timing = Timing.create("Desugared library specification conversion", this);
- machineDesugaredLibrarySpecification =
- specification.toMachineSpecification(this, app, timing);
- if (printTimes) {
- timing.report();
- }
- } catch (IOException e) {
- reporter.error(new ExceptionDiagnostic(e, Origin.unknown()));
- }
+ loadMachineDesugaredLibrarySpecification =
+ (timing, app) ->
+ machineDesugaredLibrarySpecification =
+ specification.toMachineSpecification(app, timing);
}
- public void setDesugaredLibrarySpecificationForTesting(
- DesugaredLibrarySpecification specification, Path desugaredJDKLib, Path library)
+ private ThrowingBiConsumer<Timing, DexApplication, IOException>
+ loadMachineDesugaredLibrarySpecification = null;
+
+ public void loadMachineDesugaredLibrarySpecification(Timing timing, DexApplication app)
throws IOException {
- if (specification.isEmpty()) {
+ if (loadMachineDesugaredLibrarySpecification == null) {
return;
}
- // TODO(b/221224178): Move the timing to top level timing in D8, R8 and L8.
- Timing timing = Timing.create("Desugared library specification conversion", this);
- machineDesugaredLibrarySpecification =
- specification.toMachineSpecification(this, library, timing, desugaredJDKLib);
- if (printTimes) {
- timing.report();
- }
+ timing.begin("Load machine specification");
+ loadMachineDesugaredLibrarySpecification.accept(timing, app);
+ timing.end();
}
// Contains flags describing library desugaring.
@@ -1518,6 +1514,77 @@
}
}
+ public static class OpenClosedInterfacesOptions {
+
+ public interface OpenInterfaceWitnessSuppression {
+
+ boolean isSuppressed(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ TypeElement valueType,
+ DexClass openInterface);
+ }
+
+ // Allow open interfaces by default. This is set to false in testing.
+ private boolean allowOpenInterfaces = true;
+
+ // When open interfaces are not allowed, compilation fails with an assertion error unless each
+ // open interface witness is expected according to some suppression.
+ private List<OpenInterfaceWitnessSuppression> suppressions = new ArrayList<>();
+
+ public void disallowOpenInterfaces() {
+ allowOpenInterfaces = false;
+ }
+
+ public void suppressAllOpenInterfaces() {
+ assert !allowOpenInterfaces;
+ suppressions.add((appView, valueType, openInterface) -> true);
+ }
+
+ public void suppressAllOpenInterfacesDueToMissingClasses() {
+ assert !allowOpenInterfaces;
+ suppressions.add(
+ (appView, valueType, openInterface) -> valueType.isBasedOnMissingClass(appView));
+ }
+
+ public void suppressArrayAssignmentsToJavaLangSerializable() {
+ assert !allowOpenInterfaces;
+ suppressions.add(
+ (appView, valueType, openInterface) ->
+ valueType.isArrayType()
+ && openInterface.getTypeName().equals("java.io.Serializable"));
+ }
+
+ public void suppressZipFileAssignmentsToJavaLangAutoCloseable() {
+ assert !allowOpenInterfaces;
+ suppressions.add(
+ (appView, valueType, openInterface) ->
+ valueType.isClassType()
+ && valueType
+ .asClassType()
+ .getClassType()
+ .getTypeName()
+ .equals("java.util.zip.ZipFile")
+ && openInterface.getTypeName().equals("java.lang.AutoCloseable"));
+ }
+
+ public boolean isOpenInterfacesAllowed() {
+ return allowOpenInterfaces;
+ }
+
+ public boolean hasSuppressions() {
+ return !suppressions.isEmpty();
+ }
+
+ public boolean isSuppressed(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ TypeElement valueType,
+ DexClass openInterface) {
+ return allowOpenInterfaces
+ || suppressions.stream()
+ .anyMatch(suppression -> suppression.isSuppressed(appView, valueType, openInterface));
+ }
+ }
+
public static class ApiModelTestingOptions {
public boolean enableApiCallerIdentification =
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
index cc8bc7f..f42a8cd 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.utils.SetUtils;
+import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
@@ -96,6 +97,10 @@
return backing.values().stream();
}
+ public Collection<T> toCollection() {
+ return backing.values();
+ }
+
public Set<DexEncodedMethod> toDefinitionSet() {
assert backing instanceof IdentityHashMap;
return toDefinitionSet(SetUtils::newIdentityHashSet);
diff --git a/src/test/examplesJava9/transferto/MyInputStream.java b/src/test/examplesJava9/transferto/MyInputStream.java
new file mode 100644
index 0000000..36a5e8b
--- /dev/null
+++ b/src/test/examplesJava9/transferto/MyInputStream.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2022, 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 transferto;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class MyInputStream extends ByteArrayInputStream {
+
+ public MyInputStream(byte[] buf) {
+ super(buf);
+ }
+
+ @Override
+ public long transferTo(OutputStream out) throws IOException {
+ out.write((int) '$');
+ return super.transferTo(out);
+ }
+}
diff --git a/src/test/examplesJava9/transferto/TestClass.java b/src/test/examplesJava9/transferto/TestClass.java
new file mode 100644
index 0000000..fd3c91b
--- /dev/null
+++ b/src/test/examplesJava9/transferto/TestClass.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2022, 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 transferto;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class TestClass {
+ public static void main(String[] args) throws IOException {
+ transferTo();
+ transferToOverride();
+ }
+
+ public static void transferTo() throws IOException {
+ String initialString = "Hello World!";
+ System.out.println(initialString);
+
+ try (InputStream inputStream = new ByteArrayInputStream(initialString.getBytes());
+ ByteArrayOutputStream targetStream = new ByteArrayOutputStream()) {
+ inputStream.transferTo(targetStream);
+ String copied = new String(targetStream.toByteArray());
+ System.out.println(copied);
+ }
+ }
+
+ public static void transferToOverride() throws IOException {
+ String initialString = "Hello World!";
+ System.out.println(initialString);
+
+ try (MyInputStream inputStream = new MyInputStream(initialString.getBytes());
+ ByteArrayOutputStream targetStream = new ByteArrayOutputStream()) {
+ inputStream.transferTo(targetStream);
+ String copied = new String(targetStream.toByteArray());
+ System.out.println(copied);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java b/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java
index 7a35696..28be79a 100644
--- a/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java
+++ b/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.DataResourceProvider.Visitor;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.dump.DumpOptions;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
diff --git a/src/test/java/com/android/tools/r8/CommandTestBase.java b/src/test/java/com/android/tools/r8/CommandTestBase.java
index dcfbeca..733fb9a 100644
--- a/src/test/java/com/android/tools/r8/CommandTestBase.java
+++ b/src/test/java/com/android/tools/r8/CommandTestBase.java
@@ -7,7 +7,12 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.AppForSpecConversion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
+import java.io.IOException;
import org.junit.Test;
public abstract class CommandTestBase<C extends BaseCompilerCommand> extends TestBase {
@@ -303,6 +308,20 @@
return parse(handler, prepareArgs(args));
}
+ protected InternalOptions getOptionsWithLoadedDesugaredLibraryConfiguration(
+ C command, boolean libraryCompilation) throws IOException {
+ InternalOptions options = command.getInternalOptions();
+ options.loadMachineDesugaredLibrarySpecification(
+ Timing.empty(),
+ AppForSpecConversion.readAppForTesting(
+ libraryCompilation ? ToolHelper.getDesugarJDKLibs() : null,
+ ToolHelper.getAndroidJar(AndroidApiLevel.R),
+ options,
+ libraryCompilation,
+ Timing.empty()));
+ return options;
+ }
+
/**
* Tests in this class are executed for all of D8, R8 and L8. When testing arguments this can add
* additional required arguments to all tests
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index 7a8c4e4..78be322 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -26,6 +26,7 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.collect.ImmutableList;
@@ -694,19 +695,15 @@
}
@Test
- public void desugaredLibrary() throws CompilationFailedException {
+ public void desugaredLibrary() throws CompilationFailedException, IOException {
D8Command d8Command =
parse(
"--desugared-lib",
"src/library_desugar/desugar_jdk_libs.json",
"--lib",
- ToolHelper.getAndroidJar(AndroidApiLevel.P).toString());
- assertFalse(
- d8Command
- .getInternalOptions()
- .machineDesugaredLibrarySpecification
- .getRewriteType()
- .isEmpty());
+ ToolHelper.getAndroidJar(AndroidApiLevel.R).toString());
+ InternalOptions options = getOptionsWithLoadedDesugaredLibraryConfiguration(d8Command, false);
+ assertFalse(options.machineDesugaredLibrarySpecification.getRewriteType().isEmpty());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index 2e54294..0dac7a3 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.ImmutableList;
+import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -355,19 +356,15 @@
}
@Test
- public void desugaredLibrary() throws CompilationFailedException {
+ public void desugaredLibrary() throws CompilationFailedException, IOException {
L8Command l8Command =
parse(
"--desugared-lib",
ToolHelper.getDesugarLibJsonForTesting().toString(),
"--lib",
- ToolHelper.getAndroidJar(AndroidApiLevel.P).toString());
- assertFalse(
- l8Command
- .getInternalOptions()
- .machineDesugaredLibrarySpecification
- .getRewriteType()
- .isEmpty());
+ ToolHelper.getAndroidJar(AndroidApiLevel.R).toString());
+ InternalOptions options = getOptionsWithLoadedDesugaredLibraryConfiguration(l8Command, true);
+ assertFalse(options.machineDesugaredLibrarySpecification.getRewriteType().isEmpty());
}
private void checkSingleForceAllAssertion(
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index 536f5bb..d076ec0 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.collect.ImmutableList;
@@ -825,38 +826,30 @@
}
@Test
- public void desugaredLibrary() throws CompilationFailedException {
+ public void desugaredLibrary() throws CompilationFailedException, IOException {
R8Command r8Command =
parse(
"--desugared-lib",
"src/library_desugar/desugar_jdk_libs.json",
"--lib",
- ToolHelper.getAndroidJar(AndroidApiLevel.P).toString());
- assertFalse(
- r8Command
- .getInternalOptions()
- .machineDesugaredLibrarySpecification
- .getRewriteType()
- .isEmpty());
+ ToolHelper.getAndroidJar(AndroidApiLevel.R).toString());
+ InternalOptions options = getOptionsWithLoadedDesugaredLibraryConfiguration(r8Command, false);
+ assertFalse(options.machineDesugaredLibrarySpecification.getRewriteType().isEmpty());
}
@Test
- public void desugaredLibraryWithOutputConf() throws CompilationFailedException {
+ public void desugaredLibraryWithOutputConf() throws CompilationFailedException, IOException {
Path pgout = temp.getRoot().toPath().resolve("pgout.conf");
R8Command r8Command =
parse(
"--desugared-lib",
"src/library_desugar/desugar_jdk_libs.json",
"--lib",
- ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(),
+ ToolHelper.getAndroidJar(AndroidApiLevel.R).toString(),
"--desugared-lib-pg-conf-output",
pgout.toString());
- assertFalse(
- r8Command
- .getInternalOptions()
- .machineDesugaredLibrarySpecification
- .getRewriteType()
- .isEmpty());
+ InternalOptions options = getOptionsWithLoadedDesugaredLibraryConfiguration(r8Command, false);
+ assertFalse(options.machineDesugaredLibrarySpecification.getRewriteType().isEmpty());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index ec8d314..8478b17 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -131,7 +131,7 @@
.withMinApiLevel(ToolHelper.getMinApiLevelForDexVmNoHigherThan(AndroidApiLevel.K))
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 4, "lambdadesugaring"))
.run();
}
@@ -170,7 +170,7 @@
.withMinApiLevel(AndroidApiLevel.N)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 4, "lambdadesugaring"))
.run();
}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index ab32b4f..10bc820 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -125,7 +125,7 @@
builder, rules -> box.syntheticProguardRules = rules);
libraryDesugaringTestConfiguration.configure(builder);
ToolHelper.runAndBenchmarkR8WithoutResult(
- builder.build(),
+ builder,
optionsConsumer.andThen(
options -> box.proguardConfiguration = options.getProguardConfiguration()),
benchmarkResults);
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 545f842..0f3f71d 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -8,9 +8,7 @@
import static com.android.tools.r8.ToolHelper.R8_TEST_BUCKET;
import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION;
import static com.google.common.collect.Lists.cartesianProduct;
-import static com.google.common.io.ByteStreams.toByteArray;
import static org.hamcrest.CoreMatchers.containsString;
-import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -87,6 +85,7 @@
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.base.Predicates;
import com.google.common.cache.CacheBuilder;
@@ -94,6 +93,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
import com.google.common.io.ByteStreams;
import java.io.BufferedInputStream;
import java.io.File;
@@ -110,8 +110,10 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.IdentityHashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutorService;
@@ -126,6 +128,7 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.junit.AfterClass;
+import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.rules.ExpectedException;
@@ -1943,22 +1946,102 @@
return false;
}
- public static boolean assertProgramsEqual(Path expectedJar, Path actualJar) throws Exception {
+ public static boolean assertProgramsEqual(Path expectedJar, Path actualJar) throws IOException {
if (filesAreEqual(expectedJar, actualJar)) {
return true;
}
- ArchiveClassFileProvider expected = new ArchiveClassFileProvider(expectedJar);
- ArchiveClassFileProvider actual = new ArchiveClassFileProvider(actualJar);
- assertEquals(getSortedDescriptorList(expected), getSortedDescriptorList(actual));
- for (String descriptor : expected.getClassDescriptors()) {
- assertArrayEquals(
- "Class " + descriptor + " differs",
- getClassAsBytes(expected, descriptor),
- getClassAsBytes(actual, descriptor));
- }
+ assertIdenticalInspectors(new CodeInspector(expectedJar), new CodeInspector(actualJar));
return false;
}
+ public static void assertIdenticalInspectors(CodeInspector inspector1, CodeInspector inspector2) {
+ Set<String> classes1Set =
+ inspector1.allClasses().stream()
+ .map(c -> c.getDexProgramClass().getType().toString())
+ .collect(Collectors.toSet());
+ Set<String> classes2Set =
+ inspector2.allClasses().stream()
+ .map(c -> c.getDexProgramClass().getType().toString())
+ .collect(Collectors.toSet());
+ SetView<String> classUnion = Sets.union(classes1Set, classes2Set);
+ Assert.assertEquals(
+ "Inspector 1 contains more classes",
+ Collections.emptySet(),
+ Sets.difference(classUnion, classes1Set));
+ Assert.assertEquals(
+ "Inspector 2 contains more classes",
+ Collections.emptySet(),
+ Sets.difference(classUnion, classes2Set));
+ Map<DexEncodedMethod, DexEncodedMethod> diffs = new IdentityHashMap<>();
+ for (FoundClassSubject clazz1 : inspector1.allClasses()) {
+ ClassSubject clazz = inspector2.clazz(clazz1.getOriginalName());
+ assertTrue(clazz.isPresent());
+ FoundClassSubject clazz2 = clazz.asFoundClassSubject();
+ Set<String> methods1 =
+ clazz1.allMethods().stream()
+ .map(FoundMethodSubject::toString)
+ .collect(Collectors.toSet());
+ Set<String> methods2 =
+ clazz2.allMethods().stream()
+ .map(FoundMethodSubject::toString)
+ .collect(Collectors.toSet());
+ SetView<String> union = Sets.union(methods1, methods2);
+ Assert.assertEquals(
+ "Inspector 1 contains more methods",
+ Collections.emptySet(),
+ Sets.difference(union, methods1));
+ Assert.assertEquals(
+ "Inspector 2 contains more methods",
+ Collections.emptySet(),
+ Sets.difference(union, methods2));
+ Assert.assertEquals(clazz1.allMethods().size(), clazz2.allMethods().size());
+ for (FoundMethodSubject method1 : clazz1.allMethods()) {
+ MethodSubject method = clazz2.method(method1.asMethodReference());
+ assertTrue(method.isPresent());
+ FoundMethodSubject method2 = method.asFoundMethodSubject();
+ if (method1.hasCode()) {
+ assertTrue(method2.hasCode());
+ if (!(method1
+ .getMethod()
+ .getCode()
+ .toString()
+ .equals(method2.getMethod().getCode().toString()))) {
+ diffs.put(method1.getMethod(), method2.getMethod());
+ }
+ }
+ }
+ }
+ assertTrue(printDiffs(diffs), diffs.isEmpty());
+ }
+
+ private static String printDiffs(Map<DexEncodedMethod, DexEncodedMethod> diffs) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("The following methods had differences from one dex file to the other (")
+ .append(diffs.size())
+ .append("):\n");
+ diffs.forEach(
+ (m1, m2) -> {
+ sb.append(m1.toSourceString()).append("\n");
+ String[] lines1 = m1.getCode().toString().split("\n");
+ String[] lines2 = m2.getCode().toString().split("\n");
+ if (lines1.length != lines2.length) {
+ sb.append("Different number of lines.");
+ sb.append("\n");
+ } else {
+ for (int i = 0; i < lines1.length; i++) {
+ if (!lines1[i].equals(lines2[i])) {
+ sb.append(lines1[i]);
+ sb.append("\n");
+ sb.append(lines2[i]);
+ sb.append("\n");
+ return;
+ }
+ }
+ }
+ });
+ return sb.toString();
+ }
+
public static boolean filesAreEqual(Path file1, Path file2) throws IOException {
long size = Files.size(file1);
long sizeOther = Files.size(file2);
@@ -1981,15 +2064,4 @@
}
return byteRead1 == byteRead2;
}
-
- private static List<String> getSortedDescriptorList(ArchiveClassFileProvider inputJar) {
- ArrayList<String> descriptorList = new ArrayList<>(inputJar.getClassDescriptors());
- Collections.sort(descriptorList);
- return descriptorList;
- }
-
- private static byte[] getClassAsBytes(ArchiveClassFileProvider inputJar, String descriptor)
- throws Exception {
- return toByteArray(inputJar.getProgramResource(descriptor).getByteStream());
- }
}
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index efe353d..a476d13 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -509,7 +509,7 @@
}
public CR assertNoInfoMessages() {
- getDiagnosticMessages().assertInfosCount(0);
+ getDiagnosticMessages().assertNoInfos();
return self();
}
@@ -529,7 +529,7 @@
}
public CR assertNoWarningMessages() {
- getDiagnosticMessages().assertWarningsCount(0);
+ getDiagnosticMessages().assertNoWarnings();
return self();
}
@@ -544,7 +544,7 @@
}
public CR assertNoErrorMessages() {
- getDiagnosticMessages().assertErrorsCount(0);
+ getDiagnosticMessages().assertNoErrors();
return self();
}
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index ac6ca8e..b315027 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -59,6 +59,7 @@
options.testing.reportUnusedProguardConfigurationRules = true;
options.horizontalClassMergerOptions().enable();
options.horizontalClassMergerOptions().setEnableInterfaceMerging();
+ options.getOpenClosedInterfacesOptions().disallowOpenInterfaces();
};
final Backend backend;
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index dc4d111..2b6d46b 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -1272,23 +1272,18 @@
return runR8WithFullResult(command, optionsConsumer);
}
- public static void runR8WithoutResult(
- R8Command command, Consumer<InternalOptions> optionsConsumer)
- throws CompilationFailedException {
- runAndBenchmarkR8WithoutResult(command, optionsConsumer, null);
- }
-
public static void runAndBenchmarkR8WithoutResult(
- R8Command command,
+ R8Command.Builder commandBuilder,
Consumer<InternalOptions> optionsConsumer,
BenchmarkResults benchmarkResults)
throws CompilationFailedException {
- InternalOptions internalOptions = command.getInternalOptions();
- optionsConsumer.accept(internalOptions);
long start = 0;
if (benchmarkResults != null) {
start = System.nanoTime();
}
+ R8Command command = commandBuilder.build();
+ InternalOptions internalOptions = command.getInternalOptions();
+ optionsConsumer.accept(internalOptions);
R8.runForTesting(command.getInputApp(), internalOptions);
if (benchmarkResults != null) {
long end = System.nanoTime();
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java
index a868cf1..784fce6 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java
@@ -10,13 +10,18 @@
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -35,38 +40,88 @@
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
+ private boolean isGreaterOrEqualToMockLevel() {
+ return parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(mockLevel);
+ }
+
+ private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) {
+ testBuilder
+ .addProgramClasses(Main.class, TestClass.class)
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .addAndroidBuildVersion()
+ .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
+ .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockLevel));
+ }
+
+ @Test
+ public void testD8Debug() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeTrue(
+ parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
+ testForD8()
+ .setMode(CompilationMode.DEBUG)
+ .apply(this::setupTestBuilder)
+ .compile()
+ .applyIf(isGreaterOrEqualToMockLevel(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput)
+ .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses);
+ }
+
+ @Test
+ public void testD8Release() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeFalse(
+ parameters.isCfRuntime()
+ || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
+ testForD8()
+ .setMode(CompilationMode.RELEASE)
+ // TODO(b/213552119): Remove when enabled by default.
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(this::setupTestBuilder)
+ .compile()
+ .applyIf(isGreaterOrEqualToMockLevel(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput)
+ .inspect(this::inspect);
+ }
+
@Test
public void testR8() throws Exception {
// TODO(b/197078995): Make this work on 12+.
assumeFalse(
parameters.isDexRuntime()
&& parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
- boolean isMockApiLevel =
- parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(mockLevel);
testForR8(parameters.getBackend())
- .addProgramClasses(Main.class, TestClass.class)
- .addLibraryClasses(LibraryClass.class)
- .addDefaultRuntimeLibrary(parameters)
- .setMinApi(parameters.getApiLevel())
+ .apply(this::setupTestBuilder)
.addKeepMainRule(Main.class)
- .addAndroidBuildVersion()
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
- .apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
- .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockLevel))
.enableInliningAnnotations()
.compile()
- .applyIf(isMockApiLevel, b -> b.addBootClasspathClasses(LibraryClass.class))
+ .applyIf(isGreaterOrEqualToMockLevel(), b -> b.addBootClasspathClasses(LibraryClass.class))
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLinesIf(isMockApiLevel, "LibraryClass::foo")
- .assertSuccessWithOutputLinesIf(!isMockApiLevel, "NoClassDefFoundError")
- .inspect(
- inspector ->
- verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel))
- .applyIf(
- !isMockApiLevel
- && parameters.isDexRuntime()
- && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V7_0_0),
- result -> result.assertStderrMatches(not(containsString("This dex file is invalid"))));
+ .apply(this::checkOutput)
+ .inspect(this::inspect);
+ }
+
+ private void checkOutput(SingleTestRunResult<?> runResult) {
+ if (isGreaterOrEqualToMockLevel()) {
+ runResult.assertSuccessWithOutputLines("LibraryClass::foo");
+ } else {
+ runResult.assertSuccessWithOutputLines("NoClassDefFoundError");
+ }
+ runResult.applyIf(
+ !isGreaterOrEqualToMockLevel()
+ && parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V7_0_0),
+ result -> result.assertStderrMatches(not(containsString("This dex file is invalid"))));
+ }
+
+ private void inspect(CodeInspector inspector) {
+ verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel);
}
// Only present from api level 23.
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java
index 916badd..8651a23 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java
@@ -6,13 +6,19 @@
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -31,28 +37,79 @@
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
+ private boolean isGreaterOrEqualToMockLevel() {
+ return parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(mockLevel);
+ }
+
+ private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) {
+ testBuilder
+ .addProgramClasses(Main.class)
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
+ .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockLevel));
+ }
+
+ @Test
+ public void testD8Debug() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeTrue(
+ parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
+ testForD8()
+ .setMode(CompilationMode.DEBUG)
+ .apply(this::setupTestBuilder)
+ .compile()
+ .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses)
+ .applyIf(isGreaterOrEqualToMockLevel(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ @Test
+ public void testD8Release() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeFalse(
+ parameters.isCfRuntime()
+ || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
+ testForD8()
+ .setMode(CompilationMode.RELEASE)
+ // TODO(b/213552119): Remove when enabled by default.
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(this::setupTestBuilder)
+ .compile()
+ .inspect(this::inspect)
+ .applyIf(isGreaterOrEqualToMockLevel(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
@Test
public void testR8() throws Exception {
// TODO(b/197078995): Make this work on 12+.
assumeFalse(
parameters.isDexRuntime()
&& parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
- boolean isLibraryOnBootClassPath =
- parameters.isDexRuntime()
- && parameters.getRuntime().maxSupportedApiLevel().isGreaterThanOrEqualTo(mockLevel);
testForR8(parameters.getBackend())
- .addProgramClasses(Main.class)
- .addLibraryClasses(LibraryClass.class)
- .addDefaultRuntimeLibrary(parameters)
- .setMinApi(parameters.getApiLevel())
+ .apply(this::setupTestBuilder)
.addKeepMainRule(Main.class)
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
- .apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
- .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockLevel))
.compile()
- .applyIf(isLibraryOnBootClassPath, b -> b.addBootClasspathClasses(LibraryClass.class))
+ .inspect(this::inspect)
+ .applyIf(isGreaterOrEqualToMockLevel(), b -> b.addBootClasspathClasses(LibraryClass.class))
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLinesIf(isLibraryOnBootClassPath, "Hello World");
+ .apply(this::checkOutput);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel);
+ }
+
+ private void checkOutput(SingleTestRunResult<?> runResult) {
+ runResult.assertSuccessWithOutputLinesIf(isGreaterOrEqualToMockLevel(), "Hello World");
+ runResult.assertFailureWithErrorThatThrowsIf(
+ !isGreaterOrEqualToMockLevel(), NoClassDefFoundError.class);
}
// Only present form api level 23.
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
index 51d34a9..1397da7 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
@@ -8,14 +8,19 @@
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.testing.AndroidBuildVersion;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -34,36 +39,88 @@
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
+ private boolean isGreaterOrEqualToMockLevel() {
+ return parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(mockLevel);
+ }
+
+ private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) {
+ testBuilder
+ .addProgramClasses(Main.class, TestClass.class)
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .addAndroidBuildVersion()
+ .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
+ .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockLevel));
+ }
+
+ private boolean addToBootClasspath() {
+ return parameters.isDexRuntime()
+ && parameters.getRuntime().maxSupportedApiLevel().isGreaterThanOrEqualTo(mockLevel);
+ }
+
+ @Test
+ public void testD8Debug() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeTrue(
+ parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
+ testForD8()
+ .setMode(CompilationMode.DEBUG)
+ .apply(this::setupTestBuilder)
+ .compile()
+ .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses)
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ @Test
+ public void testD8Release() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeFalse(
+ parameters.isCfRuntime()
+ || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
+ testForD8()
+ .setMode(CompilationMode.RELEASE)
+ // TODO(b/213552119): Remove when enabled by default.
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(this::setupTestBuilder)
+ .compile()
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput)
+ .inspect(this::inspect);
+ }
+
@Test
public void testR8() throws Exception {
// TODO(b/197078995): Make this work on 12+.
assumeFalse(
parameters.isDexRuntime()
&& parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
- boolean isMockApiLevel =
- parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(mockLevel);
testForR8(parameters.getBackend())
- .addProgramClasses(Main.class, TestClass.class)
- .addLibraryClasses(LibraryClass.class)
- .addDefaultRuntimeLibrary(parameters)
- .setMinApi(parameters.getApiLevel())
+ .apply(this::setupTestBuilder)
.addKeepMainRule(Main.class)
- .addAndroidBuildVersion()
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
- .apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
- .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockLevel))
.enableInliningAnnotations()
.compile()
- .applyIf(
- parameters.isDexRuntime()
- && parameters.getRuntime().maxSupportedApiLevel().isGreaterThanOrEqualTo(mockLevel),
- b -> b.addBootClasspathClasses(LibraryClass.class))
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLinesIf(isMockApiLevel, "LibraryClass::foo")
- .assertSuccessWithOutputLinesIf(!isMockApiLevel, "Hello World")
- .inspect(
- inspector ->
- verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel));
+ .apply(this::checkOutput)
+ .inspect(this::inspect);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel);
+ }
+
+ private void checkOutput(SingleTestRunResult<?> runResult) {
+ if (isGreaterOrEqualToMockLevel()) {
+ runResult.assertSuccessWithOutputLines("LibraryClass::foo");
+ } else {
+ runResult.assertSuccessWithOutputLines("Hello World");
+ }
}
// Only present from api level 23.
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
index 4f1c13b..3d6d30c 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
@@ -8,13 +8,18 @@
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.testing.AndroidBuildVersion;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -33,36 +38,88 @@
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
+ private boolean isGreaterOrEqualToMockLevel() {
+ return parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(mockLevel);
+ }
+
+ private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) {
+ testBuilder
+ .addProgramClasses(Main.class, ProgramClass.class)
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .addAndroidBuildVersion()
+ .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
+ .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockLevel));
+ }
+
+ private boolean addToBootClasspath() {
+ return parameters.isDexRuntime()
+ && parameters.getRuntime().maxSupportedApiLevel().isGreaterThanOrEqualTo(mockLevel);
+ }
+
+ @Test
+ public void testD8Debug() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeTrue(
+ parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
+ testForD8()
+ .setMode(CompilationMode.DEBUG)
+ .apply(this::setupTestBuilder)
+ .compile()
+ .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses)
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ @Test
+ public void testD8Release() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeFalse(
+ parameters.isCfRuntime()
+ || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
+ testForD8()
+ .setMode(CompilationMode.RELEASE)
+ // TODO(b/213552119): Remove when enabled by default.
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(this::setupTestBuilder)
+ .compile()
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput)
+ .inspect(this::inspect);
+ }
+
@Test
public void testR8() throws Exception {
// TODO(b/197078995): Make this work on 12+.
assumeFalse(
parameters.isDexRuntime()
&& parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
- boolean isMockApiLevel =
- parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(mockLevel);
testForR8(parameters.getBackend())
- .addProgramClasses(Main.class, ProgramClass.class)
- .addLibraryClasses(LibraryClass.class)
- .addDefaultRuntimeLibrary(parameters)
- .setMinApi(parameters.getApiLevel())
+ .apply(this::setupTestBuilder)
.addKeepMainRule(Main.class)
.addKeepClassRules(ProgramClass.class)
- .addAndroidBuildVersion()
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
- .apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
- .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockLevel))
.compile()
- .applyIf(
- parameters.isDexRuntime()
- && parameters.getRuntime().maxSupportedApiLevel().isGreaterThanOrEqualTo(mockLevel),
- b -> b.addBootClasspathClasses(LibraryClass.class))
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLinesIf(isMockApiLevel, "ProgramClass::foo")
- .assertSuccessWithOutputLinesIf(!isMockApiLevel, "Hello World")
- .inspect(
- inspector ->
- verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel));
+ .apply(this::checkOutput)
+ .inspect(this::inspect);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel);
+ }
+
+ private void checkOutput(SingleTestRunResult<?> runResult) {
+ if (isGreaterOrEqualToMockLevel()) {
+ runResult.assertSuccessWithOutputLines("ProgramClass::foo");
+ } else {
+ runResult.assertSuccessWithOutputLines("Hello World");
+ }
}
// Only present from api level 23.
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
index 94eb314..c2a6d4d 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
@@ -8,13 +8,18 @@
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.testing.AndroidBuildVersion;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -34,53 +39,113 @@
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- @Test
- public void testR8() throws Exception {
- // TODO(b/197078995): Make this work on 12+.
- assumeFalse(
- parameters.isDexRuntime()
- && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
- boolean isMockApiLevel =
- parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(mockApiLevel);
- testForR8(parameters.getBackend())
+ private boolean isGreaterOrEqualToMockLevel() {
+ return parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(mockApiLevel);
+ }
+
+ private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) {
+ testBuilder
.addProgramClasses(Main.class, ProgramClass.class)
.addLibraryClasses(LibraryClass.class, OtherLibraryClass.class, LibraryInterface.class)
.addDefaultRuntimeLibrary(parameters)
.setMinApi(parameters.getApiLevel())
- .addKeepMainRule(Main.class)
- .addKeepClassRules(ProgramClass.class)
.addAndroidBuildVersion()
.apply(ApiModelingTestHelper::enableStubbingOfClasses)
.apply(setMockApiLevelForClass(LibraryClass.class, lowerMockApiLevel))
.apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, lowerMockApiLevel))
.apply(setMockApiLevelForClass(OtherLibraryClass.class, mockApiLevel))
.apply(setMockApiLevelForDefaultInstanceInitializer(OtherLibraryClass.class, mockApiLevel))
- .apply(setMockApiLevelForClass(LibraryInterface.class, lowerMockApiLevel))
+ .apply(setMockApiLevelForClass(LibraryInterface.class, lowerMockApiLevel));
+ }
+
+ private boolean addLibraryClassesToBootClasspath() {
+ return parameters.isDexRuntime()
+ && parameters.getRuntime().maxSupportedApiLevel().isGreaterThanOrEqualTo(lowerMockApiLevel);
+ }
+
+ private boolean addOtherLibraryClassesToBootClasspath() {
+ return parameters.isDexRuntime()
+ && parameters.getRuntime().maxSupportedApiLevel().isGreaterThanOrEqualTo(mockApiLevel);
+ }
+
+ @Test
+ public void testD8Debug() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeTrue(
+ parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
+ testForD8()
+ .setMode(CompilationMode.DEBUG)
+ .apply(this::setupTestBuilder)
.compile()
+ .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses)
.applyIf(
- parameters.isDexRuntime()
- && parameters
- .getRuntime()
- .maxSupportedApiLevel()
- .isGreaterThanOrEqualTo(lowerMockApiLevel),
+ addLibraryClassesToBootClasspath(),
b -> b.addBootClasspathClasses(LibraryClass.class, LibraryInterface.class))
.applyIf(
- parameters.isDexRuntime()
- && parameters
- .getRuntime()
- .maxSupportedApiLevel()
- .isGreaterThanOrEqualTo(mockApiLevel),
+ addOtherLibraryClassesToBootClasspath(),
b -> b.addBootClasspathClasses(OtherLibraryClass.class))
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLinesIf(isMockApiLevel, "ProgramClass::foo")
- .assertSuccessWithOutputLinesIf(!isMockApiLevel, "Hello World")
- .inspect(
- inspector -> {
- verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(lowerMockApiLevel);
- verifyThat(inspector, parameters, LibraryInterface.class)
- .stubbedUntil(lowerMockApiLevel);
- verifyThat(inspector, parameters, OtherLibraryClass.class).stubbedUntil(mockApiLevel);
- });
+ .apply(this::checkOutput);
+ }
+
+ @Test
+ public void testD8Release() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeFalse(
+ parameters.isCfRuntime()
+ || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
+ testForD8()
+ .setMode(CompilationMode.RELEASE)
+ // TODO(b/213552119): Remove when enabled by default.
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(this::setupTestBuilder)
+ .compile()
+ .applyIf(
+ addLibraryClassesToBootClasspath(),
+ b -> b.addBootClasspathClasses(LibraryClass.class, LibraryInterface.class))
+ .applyIf(
+ addOtherLibraryClassesToBootClasspath(),
+ b -> b.addBootClasspathClasses(OtherLibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput)
+ .inspect(this::inspect);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeFalse(
+ parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
+ testForR8(parameters.getBackend())
+ .apply(this::setupTestBuilder)
+ .addKeepMainRule(Main.class)
+ .addKeepClassRules(ProgramClass.class)
+ .compile()
+ .applyIf(
+ addLibraryClassesToBootClasspath(),
+ b -> b.addBootClasspathClasses(LibraryClass.class, LibraryInterface.class))
+ .applyIf(
+ addOtherLibraryClassesToBootClasspath(),
+ b -> b.addBootClasspathClasses(OtherLibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .inspect(this::inspect);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(lowerMockApiLevel);
+ verifyThat(inspector, parameters, LibraryInterface.class).stubbedUntil(lowerMockApiLevel);
+ verifyThat(inspector, parameters, OtherLibraryClass.class).stubbedUntil(mockApiLevel);
+ }
+
+ private void checkOutput(SingleTestRunResult<?> runResult) {
+ if (isGreaterOrEqualToMockLevel()) {
+ runResult.assertSuccessWithOutputLines("ProgramClass::foo");
+ } else {
+ runResult.assertSuccessWithOutputLines("Hello World");
+ }
}
// Only present from api level 23.
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
index db72c72..85429ea 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
@@ -9,17 +9,23 @@
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+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.NeverInline;
import com.android.tools.r8.NoReturnTypeStrengthening;
+import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.testing.AndroidBuildVersion;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.lang.reflect.Method;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,51 +45,86 @@
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
+ private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
+ Method methodOn23 = LibraryClass.class.getDeclaredMethod("methodOn23");
+ testBuilder
+ .addProgramClasses(Main.class)
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .addAndroidBuildVersion()
+ .apply(setMockApiLevelForClass(LibraryClass.class, libraryApiLevel))
+ .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, libraryApiLevel))
+ .apply(setMockApiLevelForMethod(methodOn23, libraryApiLevel))
+ .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ .apply(ApiModelingTestHelper::enableStubbingOfClasses);
+ }
+
+ private boolean addToBootClasspath() {
+ return parameters.isDexRuntime()
+ && parameters.getRuntime().maxSupportedApiLevel().isGreaterThanOrEqualTo(libraryApiLevel);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeTrue(
+ parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
+ String result;
+ if (parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(libraryApiLevel)) {
+ result = StringUtils.lines("LibraryClass::methodOn23", "Hello World");
+ } else {
+ result = StringUtils.lines("Hello World");
+ }
+ testForD8()
+ .apply(this::setupTestBuilder)
+ .compile()
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(result)
+ // TODO(b/213552119): Add stubs to D8
+ .inspect(inspector -> inspect(inspector, false));
+ }
+
@Test
public void testR8() throws Exception {
// TODO(b/197078995): Make this work on 12+.
assumeFalse(
parameters.isDexRuntime()
&& parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
- boolean isLibraryApiLevel =
- parameters.isDexRuntime()
- && parameters.getApiLevel().isGreaterThanOrEqualTo(libraryApiLevel);
- Method methodOn23 = LibraryClass.class.getDeclaredMethod("methodOn23");
- Method mainMethod = Main.class.getDeclaredMethod("main", String[].class);
testForR8(parameters.getBackend())
- .addProgramClasses(Main.class)
- .addLibraryClasses(LibraryClass.class)
- .addDefaultRuntimeLibrary(parameters)
- .setMinApi(parameters.getApiLevel())
+ .apply(this::setupTestBuilder)
.addKeepMainRule(Main.class)
- .addAndroidBuildVersion()
- .apply(setMockApiLevelForClass(LibraryClass.class, libraryApiLevel))
- .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, libraryApiLevel))
- .apply(setMockApiLevelForMethod(methodOn23, libraryApiLevel))
- .apply(ApiModelingTestHelper::enableOutliningOfMethods)
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
.enableInliningAnnotations()
.enableNoReturnTypeStrengtheningAnnotations()
.compile()
- .applyIf(
- parameters.isDexRuntime()
- && parameters
- .getRuntime()
- .maxSupportedApiLevel()
- .isGreaterThanOrEqualTo(libraryApiLevel),
- b -> b.addBootClasspathClasses(LibraryClass.class))
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLinesIf(!isLibraryApiLevel, "Hello World")
- .assertSuccessWithOutputLinesIf(
- isLibraryApiLevel, "LibraryClass::methodOn23", "Hello World")
- .inspect(
- inspector -> {
- assertThat(inspector.method(mainMethod), isPresent());
- // TODO(b/211720912): We should never outline since the library class and method is
- // introduced at the same level.
- verifyThat(inspector, parameters, methodOn23).isNotOutlinedFrom(mainMethod);
- verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(libraryApiLevel);
- });
+ .apply(this::checkOutput)
+ .inspect(inspector -> inspect(inspector, true));
+ }
+
+ private void checkOutput(SingleTestRunResult<?> runResult) {
+ if (parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(libraryApiLevel)) {
+ runResult.assertSuccessWithOutputLines("LibraryClass::methodOn23", "Hello World");
+ } else {
+ runResult.assertSuccessWithOutputLines("Hello World");
+ }
+ }
+
+ private void inspect(CodeInspector inspector, boolean canStub) throws Exception {
+ Method methodOn23 = LibraryClass.class.getDeclaredMethod("methodOn23");
+ Method mainMethod = Main.class.getDeclaredMethod("main", String[].class);
+ assertThat(inspector.method(mainMethod), isPresent());
+ verifyThat(inspector, parameters, methodOn23).isNotOutlinedFrom(mainMethod);
+ if (canStub) {
+ verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(libraryApiLevel);
+ } else {
+ assertThat(inspector.clazz(LibraryClass.class), not(isPresent()));
+ }
}
// Only present from api level 23.
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
index 67fad04..2e96c90 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
@@ -14,9 +14,12 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
@@ -45,41 +48,59 @@
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
+ private Method addedOn23() throws Exception {
+ return LibraryClass.class.getMethod("addedOn23");
+ }
+
+ private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
+ testBuilder
+ .addProgramClasses(Main.class, TestClass.class)
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .addAndroidBuildVersion()
+ .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
+ .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, classApiLevel))
+ .apply(setMockApiLevelForMethod(addedOn23(), methodApiLevel))
+ .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ .apply(ApiModelingTestHelper::disableStubbingOfClasses);
+ }
+
+ public boolean addToBootClasspath() {
+ return parameters.isDexRuntime()
+ && parameters.getRuntime().maxSupportedApiLevel().isGreaterThanOrEqualTo(classApiLevel);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeTrue(
+ parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
+ testForD8()
+ .apply(this::setupTestBuilder)
+ .compile()
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput)
+ // TODO(b/213552119): Assert that we did not outline any methods.
+ .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses);
+ }
+
@Test
public void testR8() throws Exception {
// TODO(b/197078995): Make this work on 12+.
assumeFalse(
parameters.isDexRuntime()
&& parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
- boolean isMethodApiLevel =
- parameters.isDexRuntime()
- && parameters.getApiLevel().isGreaterThanOrEqualTo(methodApiLevel);
- Method adeddOn23 = LibraryClass.class.getMethod("addedOn23");
testForR8(parameters.getBackend())
- .addProgramClasses(Main.class, TestClass.class)
- .addLibraryClasses(LibraryClass.class)
- .addDefaultRuntimeLibrary(parameters)
- .setMinApi(parameters.getApiLevel())
+ .apply(this::setupTestBuilder)
.addKeepMainRule(Main.class)
- .addAndroidBuildVersion()
- .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
- .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, classApiLevel))
- .apply(setMockApiLevelForMethod(adeddOn23, methodApiLevel))
- .apply(ApiModelingTestHelper::enableOutliningOfMethods)
- .apply(ApiModelingTestHelper::disableStubbingOfClasses)
.enableInliningAnnotations()
.compile()
- .applyIf(
- parameters.isDexRuntime()
- && parameters
- .getRuntime()
- .maxSupportedApiLevel()
- .isGreaterThanOrEqualTo(classApiLevel),
- b -> b.addBootClasspathClasses(LibraryClass.class))
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLinesIf(!isMethodApiLevel, "Hello World")
- .assertSuccessWithOutputLinesIf(
- isMethodApiLevel, "LibraryClass::addedOn23", "LibraryClass::addedOn23", "Hello World")
+ .apply(this::checkOutput)
.inspect(
inspector -> {
int classCount =
@@ -88,7 +109,7 @@
: 3;
assertEquals(classCount, inspector.allClasses().size());
Method testMethod = TestClass.class.getDeclaredMethod("test");
- verifyThat(inspector, parameters, adeddOn23)
+ verifyThat(inspector, parameters, addedOn23())
.isOutlinedFromUntil(testMethod, methodApiLevel);
if (parameters.isDexRuntime()
&& parameters.getApiLevel().isLessThan(methodApiLevel)) {
@@ -123,6 +144,16 @@
});
}
+ private void checkOutput(SingleTestRunResult<?> runResult) {
+ if (parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(methodApiLevel)) {
+ runResult.assertSuccessWithOutputLines(
+ "LibraryClass::addedOn23", "LibraryClass::addedOn23", "Hello World");
+ } else {
+ runResult.assertSuccessWithOutputLines("Hello World");
+ }
+ }
+
// Only present from api level 19.
public static class LibraryClass {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
index 97bced2..497e0fd 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
@@ -11,9 +11,12 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
@@ -43,24 +46,12 @@
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- @Test
- public void testR8() throws Exception {
- // TODO(b/197078995): Make this work on 12+.
- assumeFalse(
- parameters.isDexRuntime()
- && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
- boolean beforeFirstApiMethodLevel =
- parameters.isCfRuntime() || parameters.getApiLevel().isLessThan(firstMethodApiLevel);
- boolean afterSecondApiMethodLevel =
- parameters.isDexRuntime()
- && parameters.getApiLevel().isGreaterThanOrEqualTo(secondMethodApiLevel);
- boolean betweenMethodApiLevels = !beforeFirstApiMethodLevel && !afterSecondApiMethodLevel;
- testForR8(parameters.getBackend())
+ private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
+ testBuilder
.addProgramClasses(Main.class, TestClass.class)
.addLibraryClasses(LibraryClass.class, OtherLibraryClass.class)
.addDefaultRuntimeLibrary(parameters)
.setMinApi(parameters.getApiLevel())
- .addKeepMainRule(Main.class)
.addAndroidBuildVersion()
.apply(setMockApiLevelForClass(LibraryClass.class, libraryClassApiLevel))
.apply(
@@ -82,30 +73,51 @@
setMockApiLevelForMethod(
OtherLibraryClass.class.getMethod("addedOn27"), secondMethodApiLevel))
.apply(ApiModelingTestHelper::enableOutliningOfMethods)
- .apply(ApiModelingTestHelper::disableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::disableStubbingOfClasses);
+ }
+
+ public boolean addToBootClasspath() {
+ return parameters.isDexRuntime()
+ && parameters
+ .getRuntime()
+ .maxSupportedApiLevel()
+ .isGreaterThanOrEqualTo(libraryClassApiLevel);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeTrue(
+ parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
+ testForD8(parameters.getBackend())
+ .apply(this::setupTestBuilder)
+ .compile()
+ .applyIf(
+ addToBootClasspath(),
+ b -> b.addBootClasspathClasses(LibraryClass.class, OtherLibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput)
+ // TODO(b/213552119): Assert that we did not outline any methods.
+ .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeFalse(
+ parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
+ testForR8(parameters.getBackend())
+ .apply(this::setupTestBuilder)
+ .addKeepMainRule(Main.class)
.enableInliningAnnotations()
.compile()
.applyIf(
- parameters.isDexRuntime()
- && parameters
- .getRuntime()
- .maxSupportedApiLevel()
- .isGreaterThanOrEqualTo(libraryClassApiLevel),
+ addToBootClasspath(),
b -> b.addBootClasspathClasses(LibraryClass.class, OtherLibraryClass.class))
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLinesIf(beforeFirstApiMethodLevel, "Hello World")
- .assertSuccessWithOutputLinesIf(
- betweenMethodApiLevels,
- "LibraryClass::addedOn23",
- "OtherLibraryClass::addedOn23",
- "Hello World")
- .assertSuccessWithOutputLinesIf(
- afterSecondApiMethodLevel,
- "LibraryClass::addedOn23",
- "LibraryClass::addedOn27",
- "OtherLibraryClass::addedOn23",
- "OtherLibraryClass::addedOn27",
- "Hello World")
+ .apply(this::checkOutput)
.inspect(
inspector -> {
// No need to check further on CF.
@@ -166,6 +178,27 @@
});
}
+ private void checkOutput(SingleTestRunResult<?> runResult) {
+ boolean beforeFirstApiMethodLevel =
+ parameters.isCfRuntime() || parameters.getApiLevel().isLessThan(firstMethodApiLevel);
+ boolean afterSecondApiMethodLevel =
+ parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(secondMethodApiLevel);
+ if (beforeFirstApiMethodLevel) {
+ runResult.assertSuccessWithOutputLines("Hello World");
+ } else if (afterSecondApiMethodLevel) {
+ runResult.assertSuccessWithOutputLines(
+ "LibraryClass::addedOn23",
+ "LibraryClass::addedOn27",
+ "OtherLibraryClass::addedOn23",
+ "OtherLibraryClass::addedOn27",
+ "Hello World");
+ } else {
+ runResult.assertSuccessWithOutputLines(
+ "LibraryClass::addedOn23", "OtherLibraryClass::addedOn23", "Hello World");
+ }
+ }
+
// Only present from api level 19.
public static class LibraryClass {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
index d71eec4..89c2f76 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
@@ -9,8 +9,11 @@
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
@@ -37,48 +40,76 @@
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
+ public Method apiMethod() throws Exception {
+ return LibraryClass.class.getDeclaredMethod("foo");
+ }
+
+ private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
+ testBuilder
+ .addProgramClasses(Main.class, TestClass.class)
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .addAndroidBuildVersion()
+ .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ .apply(setMockApiLevelForClass(LibraryClass.class, libraryClassLevel))
+ .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, libraryClassLevel))
+ .apply(setMockApiLevelForMethod(apiMethod(), libraryMethodLevel));
+ }
+
+ public boolean addToBootClasspath() {
+ return parameters.isDexRuntime()
+ && parameters.getRuntime().maxSupportedApiLevel().isGreaterThanOrEqualTo(libraryClassLevel);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeTrue(
+ parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
+ testForD8(parameters.getBackend())
+ .apply(this::setupTestBuilder)
+ .compile()
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput)
+ // TODO(b/213552119): Assert that we did not outline any methods.
+ .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses);
+ }
+
@Test
public void testR8() throws Exception {
// TODO(b/197078995): Make this work on 12+.
assumeFalse(
parameters.isDexRuntime()
&& parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
- boolean libraryClassNotStubbed =
- parameters.isDexRuntime()
- && parameters.getApiLevel().isGreaterThanOrEqualTo(libraryClassLevel);
- Method apiMethod = LibraryClass.class.getDeclaredMethod("foo");
testForR8(parameters.getBackend())
- .addProgramClasses(Main.class, TestClass.class)
- .addLibraryClasses(LibraryClass.class)
- .addDefaultRuntimeLibrary(parameters)
- .setMinApi(parameters.getApiLevel())
+ .apply(this::setupTestBuilder)
.addKeepMainRule(Main.class)
- .addAndroidBuildVersion()
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
- .apply(ApiModelingTestHelper::enableOutliningOfMethods)
- .apply(setMockApiLevelForClass(LibraryClass.class, libraryClassLevel))
- .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, libraryClassLevel))
- .apply(setMockApiLevelForMethod(apiMethod, libraryMethodLevel))
.compile()
- .applyIf(
- parameters.isDexRuntime()
- && parameters
- .getRuntime()
- .maxSupportedApiLevel()
- .isGreaterThanOrEqualTo(libraryClassLevel),
- b -> b.addBootClasspathClasses(LibraryClass.class))
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLinesIf(libraryClassNotStubbed, "LibraryClass::foo")
- .assertSuccessWithOutputLinesIf(!libraryClassNotStubbed, "Hello World")
+ .apply(this::checkOutput)
.inspect(
inspector -> {
verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(libraryClassLevel);
- verifyThat(inspector, parameters, apiMethod)
+ verifyThat(inspector, parameters, apiMethod())
.isOutlinedFromUntil(
Main.class.getDeclaredMethod("main", String[].class), libraryMethodLevel);
});
}
+ private void checkOutput(SingleTestRunResult<?> runResult) {
+ if (parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(libraryClassLevel)) {
+ runResult.assertSuccessWithOutputLines("LibraryClass::foo");
+ } else {
+ runResult.assertSuccessWithOutputLines("Hello World");
+ }
+ }
+
// Only present from api level 23.
public static class LibraryClass {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
index b537125..8ee8ad4 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
@@ -14,9 +14,12 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
@@ -45,56 +48,69 @@
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
+ private Method addedOn23() throws Exception {
+ return LibraryClass.class.getMethod("addedOn23");
+ }
+
+ private Method addedOn27() throws Exception {
+ return LibraryClass.class.getMethod("addedOn27");
+ }
+
+ private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
+ testBuilder
+ .addProgramClasses(Main.class, TestClass.class)
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .addAndroidBuildVersion()
+ .apply(setMockApiLevelForClass(LibraryClass.class, initialLibraryMockLevel))
+ .apply(
+ setMockApiLevelForDefaultInstanceInitializer(
+ LibraryClass.class, initialLibraryMockLevel))
+ .apply(setMockApiLevelForMethod(addedOn23(), initialLibraryMockLevel))
+ .apply(setMockApiLevelForMethod(addedOn27(), finalLibraryMethodLevel))
+ .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ .apply(ApiModelingTestHelper::disableStubbingOfClasses);
+ }
+
+ public boolean addToBootClasspath() {
+ return parameters.isDexRuntime()
+ && parameters
+ .getRuntime()
+ .maxSupportedApiLevel()
+ .isGreaterThanOrEqualTo(initialLibraryMockLevel);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeTrue(
+ parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
+ testForD8(parameters.getBackend())
+ .apply(this::setupTestBuilder)
+ .compile()
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput)
+ // TODO(b/213552119): Assert that we did not outline any methods.
+ .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses);
+ }
+
@Test
public void testR8() throws Exception {
// TODO(b/197078995): Make this work on 12+.
assumeFalse(
parameters.isDexRuntime()
&& parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
- boolean preMockApis =
- parameters.isCfRuntime() || parameters.getApiLevel().isLessThan(initialLibraryMockLevel);
- boolean postMockApis =
- !preMockApis && parameters.getApiLevel().isGreaterThanOrEqualTo(finalLibraryMethodLevel);
- boolean betweenMockApis = !preMockApis && !postMockApis;
- Method addedOn23 = LibraryClass.class.getMethod("addedOn23");
- Method addedOn27 = LibraryClass.class.getMethod("addedOn27");
testForR8(parameters.getBackend())
- .addProgramClasses(Main.class, TestClass.class)
- .addLibraryClasses(LibraryClass.class)
- .addDefaultRuntimeLibrary(parameters)
- .setMinApi(parameters.getApiLevel())
+ .apply(this::setupTestBuilder)
.addKeepMainRule(Main.class)
- .addAndroidBuildVersion()
- .apply(setMockApiLevelForClass(LibraryClass.class, initialLibraryMockLevel))
- .apply(
- setMockApiLevelForDefaultInstanceInitializer(
- LibraryClass.class, initialLibraryMockLevel))
- .apply(setMockApiLevelForMethod(addedOn23, initialLibraryMockLevel))
- .apply(setMockApiLevelForMethod(addedOn27, finalLibraryMethodLevel))
- .apply(ApiModelingTestHelper::enableOutliningOfMethods)
- .apply(ApiModelingTestHelper::disableStubbingOfClasses)
.enableInliningAnnotations()
.compile()
- .applyIf(
- parameters.isDexRuntime()
- && parameters
- .getRuntime()
- .maxSupportedApiLevel()
- .isGreaterThanOrEqualTo(initialLibraryMockLevel),
- b -> b.addBootClasspathClasses(LibraryClass.class))
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLinesIf(preMockApis, "Hello World")
- .assertSuccessWithOutputLinesIf(
- betweenMockApis,
- "LibraryClass::addedOn23",
- "LibraryClass::missingAndReferenced",
- "Hello World")
- .assertSuccessWithOutputLinesIf(
- postMockApis,
- "LibraryClass::addedOn23",
- "LibraryClass::missingAndReferenced",
- "LibraryCLass::addedOn27",
- "Hello World")
+ .apply(this::checkOutput)
.inspect(
inspector -> {
// No need to check further on CF.
@@ -115,8 +131,8 @@
.matches(methodSubject))
.findFirst();
assertFalse(synthesizedMissingNotReferenced.isPresent());
- verifyThat(inspector, parameters, addedOn23).isNotOutlinedFrom(testMethod);
- verifyThat(inspector, parameters, addedOn27)
+ verifyThat(inspector, parameters, addedOn23()).isNotOutlinedFrom(testMethod);
+ verifyThat(inspector, parameters, addedOn27())
.isOutlinedFromUntil(testMethod, finalLibraryMethodLevel);
verifyThat(
inspector,
@@ -131,6 +147,25 @@
});
}
+ private void checkOutput(SingleTestRunResult<?> runResult) {
+ boolean preMockApis =
+ parameters.isCfRuntime() || parameters.getApiLevel().isLessThan(initialLibraryMockLevel);
+ boolean postMockApis =
+ !preMockApis && parameters.getApiLevel().isGreaterThanOrEqualTo(finalLibraryMethodLevel);
+ if (preMockApis) {
+ runResult.assertSuccessWithOutputLines("Hello World");
+ } else if (postMockApis) {
+ runResult.assertSuccessWithOutputLines(
+ "LibraryClass::addedOn23",
+ "LibraryClass::missingAndReferenced",
+ "LibraryCLass::addedOn27",
+ "Hello World");
+ } else {
+ runResult.assertSuccessWithOutputLines(
+ "LibraryClass::addedOn23", "LibraryClass::missingAndReferenced", "Hello World");
+ }
+ }
+
// Only present from api level 23.
public static class LibraryClass {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodProtectedTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodProtectedTest.java
index 33a73cf..4428a33 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodProtectedTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodProtectedTest.java
@@ -8,10 +8,13 @@
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
@@ -41,62 +44,93 @@
return getTestParameters().withAllRuntimes().build();
}
- @Test
- public void testR8() throws Exception {
- // TODO(b/197078995): Make this work on 12+.
- assumeFalse(
- parameters.isDexRuntime()
- && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
- Method[] apiMethods =
- new Method[] {
- LibraryClass.class.getDeclaredMethod("addedOn27"),
- LibraryClass.class.getDeclaredMethod("alsoAddedOn27"),
- LibraryClass.class.getDeclaredMethod("superInvokeOn27")
- };
- AndroidApiLevel runApiLevel =
- parameters.isCfRuntime()
- ? AndroidApiLevel.B
- : parameters.getRuntime().maxSupportedApiLevel();
- boolean willInvokeLibraryMethods =
- parameters.isDexRuntime() && runApiLevel.isGreaterThanOrEqualTo(methodApiLevel);
- testForR8(parameters.getBackend())
+ private AndroidApiLevel runApiLevel() {
+ return parameters.isCfRuntime()
+ ? AndroidApiLevel.B
+ : parameters.getRuntime().maxSupportedApiLevel();
+ }
+
+ private Method[] apiMethods() throws Exception {
+ return new Method[] {
+ LibraryClass.class.getDeclaredMethod("addedOn27"),
+ LibraryClass.class.getDeclaredMethod("alsoAddedOn27"),
+ LibraryClass.class.getDeclaredMethod("superInvokeOn27")
+ };
+ }
+
+ private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
+ testBuilder
.addLibraryClasses(LibraryClass.class)
.addLibraryFiles(
parameters.isCfRuntime()
? ToolHelper.getJava8RuntimeJar()
- : ToolHelper.getFirstSupportedAndroidJar(runApiLevel))
+ : ToolHelper.getFirstSupportedAndroidJar(runApiLevel()))
.addProgramClassFileData(
transformer(TestClass.class).setClassDescriptor(TESTCLASS_DESCRIPTOR).transform(),
transformer(Main.class)
.replaceClassDescriptorInMethodInstructions(
descriptor(TestClass.class), TESTCLASS_DESCRIPTOR)
.transform())
- .addKeepMainRule(Main.class)
.setMinApi(AndroidApiLevel.B)
- .addAndroidBuildVersion(runApiLevel)
+ .addAndroidBuildVersion(runApiLevel())
.apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
.apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, classApiLevel))
.apply(
builder -> {
- for (Method apiMethod : apiMethods) {
+ for (Method apiMethod : apiMethods()) {
setMockApiLevelForMethod(apiMethod, methodApiLevel).accept(builder);
}
})
- .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ .apply(ApiModelingTestHelper::enableOutliningOfMethods);
+ }
+
+ public boolean addToBootClasspath() {
+ return parameters.isDexRuntime() && runApiLevel().isGreaterThanOrEqualTo(methodApiLevel);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeTrue(
+ parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
+ testForD8()
+ .apply(this::setupTestBuilder)
+ .compile()
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeFalse(
+ parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
+ testForR8(parameters.getBackend())
+ .apply(this::setupTestBuilder)
+ .addKeepMainRule(Main.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.compile()
- .applyIf(willInvokeLibraryMethods, b -> b.addBootClasspathClasses(LibraryClass.class))
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLinesIf(!willInvokeLibraryMethods, "Not calling API")
- .assertSuccessWithOutputLinesIf(
- willInvokeLibraryMethods,
- "Could not access LibraryClass::addedOn27",
- "LibraryClass::addedOn27",
- "LibraryClass::addedOn27",
- "LibraryCLass::alsoAddedOn27",
- "TestClass::superInvokeOn27",
- "LibraryCLass::superInvokeOn27");
+ .apply(this::checkOutput);
+ }
+
+ public void checkOutput(SingleTestRunResult<?> runResult) {
+ if (parameters.isDexRuntime() && runApiLevel().isGreaterThanOrEqualTo(methodApiLevel)) {
+ runResult.assertSuccessWithOutputLines(
+ "Could not access LibraryClass::addedOn27",
+ "LibraryClass::addedOn27",
+ "LibraryClass::addedOn27",
+ "LibraryCLass::alsoAddedOn27",
+ "TestClass::superInvokeOn27",
+ "LibraryCLass::superInvokeOn27");
+ } else {
+ runResult.assertSuccessWithOutputLines("Not calling API");
+ }
}
// Only present from api level 23.
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodUnknownTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodUnknownTest.java
index 0e9a5b3..2461448 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodUnknownTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodUnknownTest.java
@@ -5,16 +5,17 @@
package com.android.tools.r8.apimodel;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.testing.AndroidBuildVersion;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -33,38 +34,63 @@
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
+ private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) {
+ testBuilder
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .addProgramClasses(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .addAndroidBuildVersion()
+ .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
+ .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ .apply(ApiModelingTestHelper::disableStubbingOfClasses);
+ }
+
+ public boolean addToBootClasspath() {
+ return parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeTrue(
+ parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
+ testForD8()
+ .apply(this::setupTestBuilder)
+ .compile()
+ // Assert that we did not outline any methods.
+ .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses)
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
@Test
public void testR8() throws Exception {
// TODO(b/197078995): Make this work on 12+.
assumeFalse(
parameters.isDexRuntime()
&& parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
- boolean willInvokeLibraryMethods =
- parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel);
testForR8(parameters.getBackend())
- .addLibraryClasses(LibraryClass.class)
- .addDefaultRuntimeLibrary(parameters)
- .addProgramClasses(Main.class)
+ .apply(this::setupTestBuilder)
.addKeepMainRule(Main.class)
- .setMinApi(parameters.getApiLevel())
- .addAndroidBuildVersion()
- .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
- .apply(ApiModelingTestHelper::enableOutliningOfMethods)
- .apply(ApiModelingTestHelper::disableStubbingOfClasses)
.compile()
- .inspect(
- inspector -> {
- // Assert that we did not outline any methods.
- assertEquals(
- 0,
- inspector.allClasses().stream()
- .filter(FoundClassSubject::isCompilerSynthesized)
- .count());
- })
- .applyIf(willInvokeLibraryMethods, b -> b.addBootClasspathClasses(LibraryClass.class))
+ // Assert that we did not outline any methods.
+ .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses)
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLinesIf(!willInvokeLibraryMethods, "Not calling API")
- .assertSuccessWithOutputLinesIf(willInvokeLibraryMethods, "LibraryClass::unknownApiLevel");
+ .apply(this::checkOutput);
+ }
+
+ private void checkOutput(SingleTestRunResult<?> runResult) {
+ if (parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel)) {
+ runResult.assertSuccessWithOutputLines("LibraryClass::unknownApiLevel");
+ } else {
+ runResult.assertSuccessWithOutputLines("Not calling API");
+ }
}
// Only present from api level 23.
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlinePackagePrivateTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlinePackagePrivateTest.java
index f4fe8d7..9e8115d 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlinePackagePrivateTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlinePackagePrivateTest.java
@@ -6,19 +6,18 @@
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.D8TestCompileResult;
import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.testing.AndroidBuildVersion;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -45,6 +44,7 @@
@Test
public void testD8BootClassPath() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
assumeTrue(parameters.isDexRuntime());
assumeTrue(parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
compileOnD8()
@@ -72,6 +72,37 @@
.compile();
}
+ private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
+ testBuilder
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .addProgramClasses(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .addAndroidBuildVersion()
+ .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
+ .apply(
+ setMockApiLevelForMethod(
+ LibraryClass.class.getDeclaredMethod("addedOn10"), methodApiLevel))
+ .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ .apply(ApiModelingTestHelper::disableStubbingOfClasses);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeTrue(
+ parameters.isDexRuntime()
+ && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
+ testForD8()
+ .apply(this::setupTestBuilder)
+ .compile()
+ // Assert that we did not outline any methods.
+ .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses)
+ .applyIf(willInvokeLibraryMethods(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkResultOnBootClassPath);
+ }
+
@Test
public void testR8() throws Exception {
// TODO(b/197078995): Make this work on 12+.
@@ -79,29 +110,12 @@
parameters.isDexRuntime()
&& parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
testForR8(parameters.getBackend())
- .addLibraryClasses(LibraryClass.class)
- .addDefaultRuntimeLibrary(parameters)
- .addProgramClasses(Main.class)
+ .apply(this::setupTestBuilder)
.addKeepMainRule(Main.class)
- .setMinApi(parameters.getApiLevel())
.addKeepAttributeInnerClassesAndEnclosingMethod()
- .addAndroidBuildVersion()
- .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
- .apply(
- setMockApiLevelForMethod(
- LibraryClass.class.getDeclaredMethod("addedOn10"), methodApiLevel))
- .apply(ApiModelingTestHelper::enableOutliningOfMethods)
- .apply(ApiModelingTestHelper::disableStubbingOfClasses)
.compile()
- .inspect(
- inspector -> {
- // Assert that we did not outline any methods.
- assertEquals(
- 0,
- inspector.allClasses().stream()
- .filter(FoundClassSubject::isCompilerSynthesized)
- .count());
- })
+ // Assert that we did not outline any methods.
+ .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses)
.applyIf(willInvokeLibraryMethods(), b -> b.addBootClasspathClasses(LibraryClass.class))
.run(parameters.getRuntime(), Main.class)
.apply(this::checkResultOnBootClassPath);
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
index 581fca7..40f9e10 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -28,6 +28,7 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
@@ -177,6 +178,14 @@
inspector, parameters, Reference.methodFromMethod(method));
}
+ public static void assertNoSynthesizedClasses(CodeInspector inspector) {
+ assertEquals(
+ Collections.emptySet(),
+ inspector.allClasses().stream()
+ .filter(FoundClassSubject::isSynthetic)
+ .collect(Collectors.toSet()));
+ }
+
public static class ApiModelingClassVerificationHelper {
private final CodeInspector inspector;
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java
index c741f27..5dc4bd9 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollection.java
@@ -5,6 +5,7 @@
import static java.util.Collections.emptyList;
+import com.android.tools.r8.benchmarks.appdumps.TiviBenchmarks;
import com.android.tools.r8.benchmarks.desugaredlib.LegacyDesugaredLibraryBenchmark;
import com.android.tools.r8.benchmarks.helloworld.HelloWorldBenchmark;
import java.io.IOException;
@@ -45,6 +46,7 @@
// Every benchmark that should be active on golem must be setup in this method.
HelloWorldBenchmark.configs().forEach(collection::addBenchmark);
LegacyDesugaredLibraryBenchmark.configs().forEach(collection::addBenchmark);
+ TiviBenchmarks.configs().forEach(collection::addBenchmark);
return collection;
}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkTarget.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkTarget.java
index cda7f6e..0832e6c 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkTarget.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkTarget.java
@@ -4,10 +4,11 @@
package com.android.tools.r8.benchmarks;
public enum BenchmarkTarget {
+
// Possible dashboard targets on golem.
D8("d8", "D8"),
R8_COMPAT("r8-compat", "R8"),
- R8_NON_COMPAT("r8", "R8-full"),
+ R8_NON_COMPAT("r8-full", "R8-full"),
R8_FORCE_OPT("r8-force", "R8-full-minify-optimize-shrink");
private final String idName;
diff --git a/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java b/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
new file mode 100644
index 0000000..3cec248
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
@@ -0,0 +1,106 @@
+// Copyright (c) 2022, 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.benchmarks.appdumps;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestBase.Backend;
+import com.android.tools.r8.benchmarks.BenchmarkBase;
+import com.android.tools.r8.benchmarks.BenchmarkConfig;
+import com.android.tools.r8.benchmarks.BenchmarkConfigError;
+import com.android.tools.r8.benchmarks.BenchmarkDependency;
+import com.android.tools.r8.benchmarks.BenchmarkEnvironment;
+import com.android.tools.r8.benchmarks.BenchmarkMethod;
+import com.android.tools.r8.benchmarks.BenchmarkTarget;
+import com.android.tools.r8.dump.CompilerDump;
+import com.android.tools.r8.dump.DumpOptions;
+import java.io.IOException;
+import java.nio.file.Path;
+
+public class AppDumpBenchmarkBuilder {
+
+ public static AppDumpBenchmarkBuilder builder() {
+ return new AppDumpBenchmarkBuilder();
+ }
+
+ private String name;
+ private BenchmarkDependency dumpDependency;
+ private int fromRevision = -1;
+
+ public void verify() {
+ if (name == null) {
+ throw new BenchmarkConfigError("Missing name");
+ }
+ if (dumpDependency == null) {
+ throw new BenchmarkConfigError("Missing dump");
+ }
+ if (fromRevision < 0) {
+ throw new BenchmarkConfigError("Missing from-revision");
+ }
+ }
+
+ public AppDumpBenchmarkBuilder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public AppDumpBenchmarkBuilder setDumpDependencyPath(Path dumpDependencyPath) {
+ return setDumpDependency(
+ new BenchmarkDependency(
+ dumpDependencyPath.getFileName().toString(), dumpDependencyPath.getParent()));
+ }
+
+ public AppDumpBenchmarkBuilder setDumpDependency(BenchmarkDependency dependency) {
+ this.dumpDependency = dependency;
+ return this;
+ }
+
+ public AppDumpBenchmarkBuilder setFromRevision(int fromRevision) {
+ this.fromRevision = fromRevision;
+ return this;
+ }
+
+ public BenchmarkConfig build() {
+ verify();
+ return BenchmarkConfig.builder()
+ .setName(name)
+ .setTarget(BenchmarkTarget.R8_NON_COMPAT)
+ .setMethod(run(this))
+ .setFromRevision(fromRevision)
+ .addDependency(dumpDependency)
+ .measureRunTime()
+ .measureCodeSize()
+ .build();
+ }
+
+ private CompilerDump getExtractedDump(BenchmarkEnvironment environment) throws IOException {
+ Path dump = dumpDependency.getRoot(environment).resolve("dump_app.zip");
+ return CompilerDump.fromArchive(dump, environment.getTemp().newFolder().toPath());
+ }
+
+ private static BenchmarkMethod run(AppDumpBenchmarkBuilder builder) {
+ return environment ->
+ BenchmarkBase.runner(environment.getConfig())
+ .setWarmupIterations(1)
+ .run(
+ results -> {
+ CompilerDump dump = builder.getExtractedDump(environment);
+ DumpOptions dumpProperties = dump.getBuildProperties();
+ TestBase.testForR8(environment.getTemp(), Backend.DEX)
+ // TODO(b/221811893): mock a typical setup of program providers from agp.
+ .addProgramFiles(dump.getProgramArchive())
+ .addLibraryFiles(dump.getLibraryArchive())
+ .addKeepRuleFiles(dump.getProguardConfigFile())
+ .setMinApi(dumpProperties.getMinApi())
+ .allowUnusedDontWarnPatterns()
+ .allowUnusedProguardConfigurationRules()
+ // TODO(b/222228826): Disallow unrecognized diagnostics and open interfaces.
+ .allowDiagnosticMessages()
+ .addOptionsModification(
+ options ->
+ options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
+ .benchmarkCompile(results)
+ .benchmarkCodeSize(results);
+ });
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/appdumps/TiviBenchmarks.java b/src/test/java/com/android/tools/r8/benchmarks/appdumps/TiviBenchmarks.java
new file mode 100644
index 0000000..235c559
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/appdumps/TiviBenchmarks.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2022, 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.benchmarks.appdumps;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.benchmarks.BenchmarkBase;
+import com.android.tools.r8.benchmarks.BenchmarkConfig;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TiviBenchmarks extends BenchmarkBase {
+
+ private static final Path dump = Paths.get("third_party", "opensource-apps", "tivi");
+
+ public TiviBenchmarks(BenchmarkConfig config, TestParameters parameters) {
+ super(config, parameters);
+ }
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> data() {
+ return parametersFromConfigs(configs());
+ }
+
+ public static List<BenchmarkConfig> configs() {
+ return ImmutableList.of(
+ AppDumpBenchmarkBuilder.builder()
+ .setName("Tivi")
+ .setDumpDependencyPath(dump)
+ .setFromRevision(12170)
+ .build());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTestRunner.java b/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTestRunner.java
index 5d84adf..7b52abd 100644
--- a/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTestRunner.java
@@ -11,7 +11,6 @@
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 com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -79,7 +78,9 @@
testForR8Compat(parameters.getBackend())
.addProgramClasses(getClasses())
.setMinApi(parameters.getApiLevel())
- .addKeepMainRule(getMainClass());
+ .addKeepMainRule(getMainClass())
+ .addOptionsModification(
+ options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces());
if (addKeepDeserializedLambdaRule) {
builder.allowUnusedProguardConfigurationRules(parameters.isDexRuntime());
builder.addKeepRules(
diff --git a/src/test/java/com/android/tools/r8/cf/PrintSeedsWithDeserializeLambdaMethodTest.java b/src/test/java/com/android/tools/r8/cf/PrintSeedsWithDeserializeLambdaMethodTest.java
index f5673dd..33b13a9 100644
--- a/src/test/java/com/android/tools/r8/cf/PrintSeedsWithDeserializeLambdaMethodTest.java
+++ b/src/test/java/com/android/tools/r8/cf/PrintSeedsWithDeserializeLambdaMethodTest.java
@@ -9,7 +9,6 @@
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 com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -54,6 +53,9 @@
.addProgramClasses(getClasses())
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(getMainClass())
+ // TODO(b/214496607): Improve analysis precision to ensure there are no open interfaces.
+ .addOptionsModification(
+ options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
.addPrintSeeds()
.allowStdoutMessages()
.noMinification()
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
index 12bbdf3..591b8ce 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
@@ -99,6 +99,11 @@
.setMode(mode)
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
.addKeepRuleFiles(MAIN_KEEP)
+ .addOptionsModification(
+ options ->
+ options
+ .getOpenClosedInterfacesOptions()
+ .suppressZipFileAssignmentsToJavaLangAutoCloseable())
.compile()
.apply(c -> FileUtils.writeTextFile(map, c.getProguardMap()))
.writeToZip(jar);
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java
index 9fa9260..7081080 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java
@@ -18,8 +18,8 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -46,13 +46,7 @@
// I and J are not eligible for merging, since the lambda that implements I & J inherits a
// default m() method from K, which is also on J.
.addHorizontallyMergedClassesInspector(
- inspector -> {
- if (parameters.isCfRuntime()) {
- inspector.assertIsCompleteMergeGroup(I.class, J.class);
- } else {
- inspector.assertNoClassesMerged();
- }
- })
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
@@ -66,25 +60,13 @@
ClassSubject aClassSubject = inspector.clazz(A.class);
if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
assertThat(aClassSubject, isPresent());
- if (parameters.isCfRuntime()) {
- assertThat(aClassSubject, isImplementing(inspector.clazz(I.class)));
- } else {
- assertThat(aClassSubject, isImplementing(inspector.clazz(J.class)));
- }
+ assertThat(aClassSubject, isImplementing(inspector.clazz(J.class)));
} else {
assertThat(aClassSubject, isAbsent());
}
})
.run(parameters.getRuntime(), Main.class)
- // TODO(b/173990042): Should succeed with "K", "J".
- .applyIf(
- parameters.isCfRuntime(),
- builder ->
- builder.assertFailureWithErrorThatThrows(
- parameters.isCfRuntime(CfVm.JDK11)
- ? AbstractMethodError.class
- : IncompatibleClassChangeError.class),
- builder -> builder.assertSuccessWithOutputLines("K", "J"));
+ .assertSuccessWithOutputLines("K", "J");
}
static class Main {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithIntersectionMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithIntersectionMergingTest.java
index 91f0fbe..7125324 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithIntersectionMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithIntersectionMergingTest.java
@@ -33,8 +33,11 @@
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
- .addHorizontallyMergedClassesInspector(
- inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
+ .applyIf(
+ parameters.isDexRuntime(),
+ testBuilder ->
+ testBuilder.addHorizontallyMergedClassesInspector(
+ inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class)))
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
.noClassInliningOfSynthetics()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesWithIntersectionMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesWithIntersectionMergingTest.java
index 44bda30..6687fac 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesWithIntersectionMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesWithIntersectionMergingTest.java
@@ -33,8 +33,11 @@
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
- .addHorizontallyMergedClassesInspector(
- inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
+ .applyIf(
+ parameters.isDexRuntime(),
+ testBuilder ->
+ testBuilder.addHorizontallyMergedClassesInspector(
+ inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class)))
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
.noClassInliningOfSynthetics()
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
index 24e1de1..1ca2f2b 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.compilerapi.assertionconfiguration.AssertionConfigurationTest;
+import com.android.tools.r8.compilerapi.inputdependencies.InputDependenciesTest;
import com.android.tools.r8.compilerapi.mapid.CustomMapIdTest;
import com.android.tools.r8.compilerapi.mockdata.MockClass;
import com.android.tools.r8.compilerapi.mockdata.MockClassWithAssertion;
@@ -34,7 +35,8 @@
CustomSourceFileTest.ApiTest.class);
private static final List<Class<? extends CompilerApiTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
- ImmutableList.of(AssertionConfigurationTest.ApiTest.class);
+ ImmutableList.of(
+ AssertionConfigurationTest.ApiTest.class, InputDependenciesTest.ApiTest.class);
private final TemporaryFolder temp;
@@ -73,6 +75,7 @@
}
// The API tests always link against the jar that the test runner is using.
+ @Override
public Path getTargetJar() {
return isTestingR8Lib() ? R8LIB_JAR : R8_JAR;
}
diff --git a/src/test/java/com/android/tools/r8/compilerapi/inputdependencies/InputDependenciesTest.java b/src/test/java/com/android/tools/r8/compilerapi/inputdependencies/InputDependenciesTest.java
new file mode 100644
index 0000000..f6161fc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compilerapi/inputdependencies/InputDependenciesTest.java
@@ -0,0 +1,183 @@
+// Copyright (c) 2022, 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.compilerapi.inputdependencies;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.InputDependencyGraphConsumer;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.compilerapi.CompilerApiTest;
+import com.android.tools.r8.compilerapi.CompilerApiTestRunner;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.utils.BooleanBox;
+import com.android.tools.r8.utils.IntBox;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.Arrays;
+import java.util.Collections;
+import org.junit.Test;
+
+public class InputDependenciesTest extends CompilerApiTestRunner {
+
+ public InputDependenciesTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<? extends CompilerApiTest> binaryTestClass() {
+ return ApiTest.class;
+ }
+
+ @Test
+ public void testInputDependencies() throws Exception {
+ ApiTest test = new ApiTest(ApiTest.PARAMETERS);
+ runTest(test::run);
+ }
+
+ private void checkPathOrigin(Path expected, Origin origin, Path base) {
+ assertTrue(origin instanceof PathOrigin);
+ assertEquals(base.relativize(expected), base.relativize(((PathOrigin) origin).getPath()));
+ }
+
+ private interface Runner {
+ void run(Path includePath, Path applymappingPath, InputDependencyGraphConsumer consumer)
+ throws Exception;
+ }
+
+ private void runTest(Runner test) throws Exception {
+ Path dir = temp.newFolder().toPath();
+ Path included1 =
+ Files.write(
+ dir.resolve("included1.rules"), Collections.emptyList(), StandardOpenOption.CREATE_NEW);
+ Path included2 =
+ Files.write(
+ dir.resolve("included2.rules"),
+ Arrays.asList("-include included1.rules"),
+ StandardOpenOption.CREATE_NEW);
+ Path applymapping =
+ Files.write(
+ dir.resolve("mymapping.txt"), Collections.emptyList(), StandardOpenOption.CREATE_NEW);
+
+ IntBox dependencyCount = new IntBox(0);
+ BooleanBox isFinished = new BooleanBox(false);
+ test.run(
+ included2,
+ applymapping,
+ new InputDependencyGraphConsumer() {
+ @Override
+ public void accept(Origin dependent, Path dependency) {
+ dependencyCount.increment();
+ assertEquals(Origin.unknown(), dependent);
+ assertEquals(dir.relativize(applymapping), dir.relativize(dependency));
+ }
+
+ @Override
+ public void acceptProguardInclude(Origin dependent, Path dependency) {
+ dependencyCount.increment();
+ if (dependent == Origin.unknown()) {
+ assertEquals(dir.relativize(included2), dir.relativize(dependency));
+ } else {
+ checkPathOrigin(included2, dependent, dir);
+ assertEquals(dir.relativize(included1), dir.relativize(dependency));
+ }
+ }
+
+ @Override
+ public void finished() {
+ isFinished.set(true);
+ }
+ });
+ assertEquals(3, dependencyCount.get());
+ assertTrue(isFinished.get());
+ }
+
+ public static class ApiTest extends CompilerApiTest {
+
+ public ApiTest(Object parameters) {
+ super(parameters);
+ }
+
+ public void run(
+ Path includedFile, Path applymapping, InputDependencyGraphConsumer dependencyConsumer)
+ throws Exception {
+ R8.run(
+ R8Command.builder()
+ .addClassProgramData(getBytesForClass(getMockClass()), Origin.unknown())
+ .addProguardConfiguration(getKeepMainRules(getMockClass()), Origin.unknown())
+ .addProguardConfiguration(
+ Arrays.asList("-include " + includedFile, "-applymapping" + applymapping),
+ Origin.unknown())
+ .addLibraryFiles(getJava8RuntimeJar())
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .setInputDependencyGraphConsumer(dependencyConsumer)
+ .build());
+ }
+
+ @Test
+ public void testInputDependencies() throws Exception {
+ Path emptyFile =
+ Files.write(
+ temp.newFolder().toPath().resolve("empty.txt"),
+ Collections.emptyList(),
+ StandardOpenOption.CREATE_NEW);
+ run(
+ emptyFile,
+ emptyFile,
+ new InputDependencyGraphConsumer() {
+ @Override
+ public void accept(Origin dependent, Path dependency) {
+ // ignore.
+ }
+
+ @Override
+ public void acceptProguardInclude(Origin dependent, Path dependency) {
+ accept(dependent, dependency);
+ }
+
+ @Override
+ public void acceptProguardInJars(Origin dependent, Path dependency) {
+ accept(dependent, dependency);
+ }
+
+ @Override
+ public void acceptProguardLibraryJars(Origin dependent, Path dependency) {
+ accept(dependent, dependency);
+ }
+
+ @Override
+ public void acceptProguardApplyMapping(Origin dependent, Path dependency) {
+ accept(dependent, dependency);
+ }
+
+ @Override
+ public void acceptProguardObfuscationDictionary(Origin dependent, Path dependency) {
+ accept(dependent, dependency);
+ }
+
+ @Override
+ public void acceptProguardClassObfuscationDictionary(
+ Origin dependent, Path dependency) {
+ accept(dependent, dependency);
+ }
+
+ @Override
+ public void acceptProguardPackageObfuscationDictionary(
+ Origin dependent, Path dependency) {
+ accept(dependent, dependency);
+ }
+
+ @Override
+ public void finished() {
+ // ignore.
+ }
+ });
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
index 2453bef..f6f59e8 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
@@ -293,9 +293,9 @@
Set<MethodReference> expectedSynthetics =
ImmutableSet.of(
SyntheticItemsTestUtils.syntheticBackportMethod(
- User1.class, 0, Character.class.getMethod("compare", char.class, char.class)),
+ User1.class, 0, Boolean.class.getMethod("compare", boolean.class, boolean.class)),
SyntheticItemsTestUtils.syntheticBackportMethod(
- User1.class, 1, Boolean.class.getMethod("compare", boolean.class, boolean.class)),
+ User1.class, 1, Character.class.getMethod("compare", char.class, char.class)),
SyntheticItemsTestUtils.syntheticBackportMethod(
User2.class, 0, Integer.class.getMethod("compare", int.class, int.class)));
assertEquals(expectedSynthetics, getSyntheticMethods(inspector));
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
index 2684b3b..3b41b98 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
@@ -4,10 +4,12 @@
package com.android.tools.r8.desugar.desugaredlibrary;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static junit.framework.TestCase.assertTrue;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -63,7 +65,6 @@
// Expected since we are compiling with an invalid set-up.
}
TestDiagnosticMessages diagnosticMessages = testBuilder.getState().getDiagnosticsMessages();
- diagnosticMessages.assertOnlyWarnings();
assertTrue(
diagnosticMessages
.getWarnings()
@@ -119,7 +120,13 @@
ToolHelper.runL8(l8Builder.build(), options -> {});
CodeInspector codeInspector = new CodeInspector(desugaredLib);
assertCorrect(codeInspector);
- diagnosticsHandler.assertNoMessages();
+ if (isJDK11DesugaredLibrary()) {
+ diagnosticsHandler.assertNoErrors();
+ diagnosticsHandler.assertAllWarningsMatch(
+ diagnosticMessage(containsString("Specification conversion")));
+ } else {
+ diagnosticsHandler.assertNoMessages();
+ }
}
private void assertCorrect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryDeterminismTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryDeterminismTest.java
index 9623e7c..5f060f1 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryDeterminismTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryDeterminismTest.java
@@ -3,30 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.desugar.desugaredlibrary;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
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.graph.DexEncodedMethod;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.ZipUtils;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
-import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.collect.Sets;
-import com.google.common.collect.Sets.SetView;
-import java.io.IOException;
-import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.Collections;
-import java.util.IdentityHashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,98 +32,10 @@
public void testDeterminism() throws Exception {
AndroidApiLevel minApiLevel = parameters.getRuntime().asDex().getMinApiLevel();
Assume.assumeTrue(minApiLevel.isLessThan(AndroidApiLevel.O));
- Path libDexFile1 = buildDesugaredLibraryToBytes(minApiLevel);
- Path libDexFile2 = buildDesugaredLibraryToBytes(minApiLevel);
- assertIdenticalInspectors(libDexFile1, libDexFile2);
- assertArrayEquals(getDexBytes(libDexFile1), getDexBytes(libDexFile2));
- }
-
- private void assertIdenticalInspectors(Path libDexFile1, Path libDexFile2) throws IOException {
- CodeInspector i1 = new CodeInspector(libDexFile1.resolve("classes.dex"));
- CodeInspector i2 = new CodeInspector(libDexFile2.resolve("classes.dex"));
- assertIdenticalInspectors(i1, i2);
- }
-
- public static void assertIdenticalInspectors(CodeInspector i1, CodeInspector i2) {
- assertEquals(i1.allClasses().size(), i2.allClasses().size());
- Map<DexEncodedMethod, DexEncodedMethod> diffs = new IdentityHashMap<>();
- for (FoundClassSubject clazz1 : i1.allClasses()) {
- ClassSubject clazz = i2.clazz(clazz1.getOriginalName());
- assertTrue(clazz.isPresent());
- FoundClassSubject clazz2 = clazz.asFoundClassSubject();
- Set<String> methods1 =
- clazz1.allMethods().stream()
- .map(FoundMethodSubject::toString)
- .collect(Collectors.toSet());
- Set<String> methods2 =
- clazz2.allMethods().stream()
- .map(FoundMethodSubject::toString)
- .collect(Collectors.toSet());
- SetView<String> union = Sets.union(methods1, methods2);
- assertEquals(
- "Inspector 1 contains more methods",
- Collections.emptySet(),
- Sets.difference(union, methods1));
- assertEquals(
- "Inspector 2 contains more methods",
- Collections.emptySet(),
- Sets.difference(union, methods2));
- assertEquals(clazz1.allMethods().size(), clazz2.allMethods().size());
- for (FoundMethodSubject method1 : clazz1.allMethods()) {
- MethodSubject method = clazz2.method(method1.asMethodReference());
- assertTrue(method.isPresent());
- FoundMethodSubject method2 = method.asFoundMethodSubject();
- if (method1.hasCode()) {
- assertTrue(method2.hasCode());
- if (!(method1
- .getMethod()
- .getCode()
- .toString()
- .equals(method2.getMethod().getCode().toString()))) {
- diffs.put(method1.getMethod(), method2.getMethod());
- }
- }
- }
- }
- assertTrue(printDiffs(diffs), diffs.isEmpty());
- }
-
- private static String printDiffs(Map<DexEncodedMethod, DexEncodedMethod> diffs) {
- StringBuilder sb = new StringBuilder();
- sb.append("The following methods had differences from one dex file to the other (")
- .append(diffs.size())
- .append("):\n");
- diffs.forEach(
- (m1, m2) -> {
- sb.append(m1.toSourceString()).append("\n");
- String[] lines1 = m1.getCode().toString().split("\n");
- String[] lines2 = m2.getCode().toString().split("\n");
- if (lines1.length != lines2.length) {
- sb.append("Different number of lines.");
- sb.append("\n");
- } else {
- for (int i = 0; i < lines1.length; i++) {
- if (!lines1[i].equals(lines2[i])) {
- sb.append(lines1[i]);
- sb.append("\n");
- sb.append(lines2[i]);
- sb.append("\n");
- return;
- }
- }
- }
- });
- return sb.toString();
- }
-
- private byte[] getDexBytes(Path libDexFile) throws IOException {
- return Files.readAllBytes(libDexFile.resolve("classes.dex"));
- }
-
- private Path buildDesugaredLibraryToBytes(AndroidApiLevel minApiLevel) throws IOException {
- Path lib1 = buildDesugaredLibrary(minApiLevel);
- Path unzipped1 = temp.newFolder().toPath();
- ZipUtils.unzip(lib1.toString(), unzipped1.toFile());
- return unzipped1;
+ Path libDexFile1 = buildDesugaredLibrary(minApiLevel);
+ Path libDexFile2 = buildDesugaredLibrary(minApiLevel);
+ uploadJarsToCloudStorageIfTestFails(TestBase::filesAreEqual, libDexFile1, libDexFile2);
+ assertProgramsEqual(libDexFile1, libDexFile2);
+ assertTrue(filesAreEqual(libDexFile1, libDexFile2));
}
}
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 56f687c..fb5922a 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
@@ -69,14 +69,7 @@
public void setDesugaredLibrarySpecificationForTesting(
InternalOptions options, DesugaredLibrarySpecification specification) {
- try {
- options.setDesugaredLibrarySpecificationForTesting(
- specification,
- ToolHelper.getDesugarJDKLibs(),
- ToolHelper.getAndroidJar(AndroidApiLevel.R));
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
+ options.setDesugaredLibrarySpecification(specification);
}
// For conversions tests, we need DexRuntimes where classes to convert are present (DexRuntimes
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java
index 0c61a06..1a7981a 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java
@@ -4,6 +4,9 @@
package com.android.tools.r8.desugar.desugaredlibrary;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.core.StringContains.containsString;
+
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.L8Command;
import com.android.tools.r8.OutputMode;
@@ -65,6 +68,12 @@
Arrays.asList(FUNCTION_KEEP.split(System.lineSeparator())), Origin.unknown());
}
ToolHelper.runL8(l8Builder.build(), options -> {});
- diagnosticsHandler.assertNoMessages();
+ if (isJDK11DesugaredLibrary()) {
+ diagnosticsHandler.assertNoErrors();
+ diagnosticsHandler.assertAllWarningsMatch(
+ diagnosticMessage(containsString("Specification conversion")));
+ } else {
+ diagnosticsHandler.assertNoMessages();
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetAndBackportTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetAndBackportTest.java
index 27762ea..50f2ce7 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetAndBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetAndBackportTest.java
@@ -7,7 +7,6 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyRewritingFlags;
import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyTopLevelFlags;
@@ -15,7 +14,6 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import java.io.IOException;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -55,15 +53,9 @@
.putRewritePrefix("java.time.", "j$.time.")
.putBackportCoreLibraryMember("java.lang.DesugarMath", "java.lang.Math")
.build();
- try {
- options.setDesugaredLibrarySpecificationForTesting(
- new LegacyDesugaredLibrarySpecification(
- LegacyTopLevelFlags.testing(), rewritingFlags, true),
- ToolHelper.getDesugarJDKLibs(),
- ToolHelper.getAndroidJar(AndroidApiLevel.R));
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
+ options.setDesugaredLibrarySpecification(
+ new LegacyDesugaredLibrarySpecification(
+ LegacyTopLevelFlags.testing(), rewritingFlags, true));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SpliteratorConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SpliteratorConversionTest.java
new file mode 100644
index 0000000..2e0d110
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SpliteratorConversionTest.java
@@ -0,0 +1,126 @@
+// Copyright (c) 2022, 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.desugaredlibrary.conversiontests;
+
+import com.android.tools.r8.TestParameters;
+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.StringUtils;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Spliterator;
+import org.junit.BeforeClass;
+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 SpliteratorConversionTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+
+ private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.N;
+ private static final String EXPECTED_RESULT = StringUtils.lines("2", "true", "2", "true");
+
+ private static Path CUSTOM_LIB;
+
+ @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getConversionParametersUpToExcluding(MIN_SUPPORTED), BooleanUtils.values());
+ }
+
+ public SpliteratorConversionTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @BeforeClass
+ public static void compileCustomLib() throws Exception {
+ CUSTOM_LIB =
+ testForD8(getStaticTemp())
+ .addProgramClasses(CustomLibClass.class)
+ .setMinApi(MIN_SUPPORTED)
+ .compile()
+ .writeToZip();
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8()
+ .addLibraryFiles(getLibraryFile())
+ .setMinApi(parameters.getApiLevel())
+ .addProgramClasses(Executor.class)
+ .addLibraryClasses(CustomLibClass.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .addRunClasspathFiles(CUSTOM_LIB)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(parameters.getBackend())
+ .addLibraryFiles(getLibraryFile())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Executor.class)
+ .addProgramClasses(Executor.class)
+ .addLibraryClasses(CustomLibClass.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .addRunClasspathFiles(CUSTOM_LIB)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ static class Executor {
+
+ public static void main(String[] args) {
+ ArrayList<String> strings = new ArrayList<>();
+ strings.add("A");
+ strings.add("B");
+ CustomLibClass.printSpliterator(strings.spliterator());
+
+ Spliterator<String> split = CustomLibClass.get();
+ System.out.println(split.getExactSizeIfKnown());
+ System.out.println(split.hasCharacteristics(Spliterator.SIZED));
+ }
+ }
+
+ // This class will be put at compilation time as library and on the runtime class path.
+ // This class is convenient for easy testing. Each method plays the role of methods in the
+ // platform APIs for which argument/return values need conversion.
+ static class CustomLibClass {
+
+ public static void printSpliterator(Spliterator<String> split) {
+ System.out.println(split.getExactSizeIfKnown());
+ System.out.println(split.hasCharacteristics(Spliterator.SIZED));
+ }
+
+ public static Spliterator<String> get() {
+ ArrayList<String> strings = new ArrayList<>();
+ strings.add("A");
+ strings.add("B");
+ return strings.spliterator();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/InputStreamTransferToTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/InputStreamTransferToTest.java
new file mode 100644
index 0000000..77f16d2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/InputStreamTransferToTest.java
@@ -0,0 +1,82 @@
+// Copyright (c) 2022, 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.desugaredlibrary.jdk11;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import org.junit.Assume;
+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 InputStreamTransferToTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+
+ private static final Path INPUT_JAR =
+ Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR + "transferto.jar");
+ private static final String EXPECTED_OUTPUT =
+ StringUtils.lines("Hello World!", "Hello World!", "Hello World!", "$Hello World!");
+ private static final String MAIN_CLASS = "transferto.TestClass";
+
+ @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ public InputStreamTransferToTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ Assume.assumeTrue(isJDK11DesugaredLibrary());
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8(parameters.getBackend())
+ .addLibraryFiles(getLibraryFile())
+ .addProgramFiles(INPUT_JAR)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ Assume.assumeTrue(isJDK11DesugaredLibrary());
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(parameters.getBackend())
+ .addLibraryFiles(getLibraryFile())
+ .addProgramFiles(INPUT_JAR)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(MAIN_CLASS)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
index 01eb3b8..592dab4 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
@@ -158,15 +158,18 @@
.toArray(new String[0]));
printTime("R8/JVM external", start);
assertEquals(javaProcessResult.toString(), 0, javaProcessResult.exitCode);
- assertTrue(
+ uploadJarsToCloudStorageIfTestFails(
+ TestBase::filesAreEqual, outputThroughCf, outputThroughCfExternal);
+ assertProgramsEqual(outputThroughCf, outputThroughCfExternal);
+ String message =
"The output of R8/JVM in-process and R8/JVM external differ."
+ " Make sure you have an up-to-date compilation of "
+ r8jar
+ ". If not, that could very likely cause the in-process run (eg, via intellij) to"
+ " differ from the external run which uses "
+ r8jar
- + ". If up-to-date, the likely cause of this error is that R8 is non-deterministic.",
- TestBase.filesAreEqual(outputThroughCf, outputThroughCfExternal));
+ + ". If up-to-date, the likely cause of this error is that R8 is non-deterministic.";
+ assertTrue(message, filesAreEqual(outputThroughCf, outputThroughCfExternal));
}
// Finally compile R8 on the ART runtime using the already compiled DEX version of R8.
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java b/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java
index de2364e..c9d943e 100644
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreLatestTest.java
@@ -64,9 +64,13 @@
compileWithR8(
builder ->
builder.addOptionsModification(
- options ->
- options.testing.processingContextsConsumer =
- id -> assertNull(idsRoundOne.put(id, id))));
+ options -> {
+ options
+ .getOpenClosedInterfacesOptions()
+ .suppressArrayAssignmentsToJavaLangSerializable();
+ options.testing.processingContextsConsumer =
+ id -> assertNull(idsRoundOne.put(id, id));
+ }));
compileResult.runDex2Oat(parameters.getRuntime()).assertNoVerificationErrors();
@@ -75,12 +79,16 @@
compileWithR8(
builder ->
builder.addOptionsModification(
- options ->
- options.testing.processingContextsConsumer =
- id -> {
- AssertionUtils.assertNotNull(idsRoundOne.get(id));
- assertNull(idsRoundTwo.put(id, id));
- }));
+ options -> {
+ options
+ .getOpenClosedInterfacesOptions()
+ .suppressArrayAssignmentsToJavaLangSerializable();
+ options.testing.processingContextsConsumer =
+ id -> {
+ AssertionUtils.assertNotNull(idsRoundOne.get(id));
+ assertNull(idsRoundTwo.put(id, id));
+ };
+ }));
// Verify that the result of the two compilations was the same.
assertEquals(
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java b/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java
index 0622a2a..34b77bd 100644
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java
@@ -62,9 +62,13 @@
compileWithR8(
builder ->
builder.addOptionsModification(
- options ->
- options.testing.processingContextsConsumer =
- id -> assertTrue(idsRoundOne.add(id))));
+ options -> {
+ options
+ .getOpenClosedInterfacesOptions()
+ .suppressArrayAssignmentsToJavaLangSerializable();
+ options.testing.processingContextsConsumer =
+ id -> assertTrue(idsRoundOne.add(id));
+ }));
compileResult.runDex2Oat(parameters.getRuntime()).assertNoVerificationErrors();
@@ -73,9 +77,13 @@
compileWithR8(
builder ->
builder.addOptionsModification(
- options ->
- options.testing.processingContextsConsumer =
- id -> assertTrue(idsRoundTwo.add(id))));
+ options -> {
+ options
+ .getOpenClosedInterfacesOptions()
+ .suppressArrayAssignmentsToJavaLangSerializable();
+ options.testing.processingContextsConsumer =
+ id -> assertTrue(idsRoundTwo.add(id));
+ }));
uploadJarsToCloudStorageIfTestFails(
(ignored1, ignored2) -> {
@@ -94,7 +102,12 @@
compileWithR8(
builder ->
builder.addOptionsModification(
- options -> options.testing.forceJumboStringProcessing = true))
+ options -> {
+ options.testing.forceJumboStringProcessing = true;
+ options
+ .getOpenClosedInterfacesOptions()
+ .suppressArrayAssignmentsToJavaLangSerializable();
+ }))
.runDex2Oat(parameters.getRuntime())
.assertNoVerificationErrors();
}
diff --git a/src/test/java/com/android/tools/r8/internal/proto/ChromeProtoRewritingTest.java b/src/test/java/com/android/tools/r8/internal/proto/ChromeProtoRewritingTest.java
index ff19143..f9822e9 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/ChromeProtoRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/ChromeProtoRewritingTest.java
@@ -49,6 +49,8 @@
keepDynamicMethodSignatureRule(),
keepNewMessageInfoSignatureRule())
.addDontWarn("android.content.pm.IPackageManager")
+ .addOptionsModification(
+ options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
.allowUnusedDontWarnPatterns()
.allowUnusedProguardConfigurationRules()
.enableProtoShrinking(false)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ArrayPutToInterfaceWithObjectMergingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ArrayPutToInterfaceWithObjectMergingTest.java
index 1411ce8..1107d60 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ArrayPutToInterfaceWithObjectMergingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ArrayPutToInterfaceWithObjectMergingTest.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoVerticalClassMerging;
-import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
@@ -56,7 +55,13 @@
.addKeepRules("-keepclassmembers class " + Main.class.getTypeName() + " { *** get(...); }")
.addNoVerticalClassMergingAnnotations()
.applyIf(
- !enableVerticalClassMerging, R8TestBuilder::enableNoVerticalClassMergingAnnotations)
+ !enableVerticalClassMerging,
+ testBuilder ->
+ testBuilder
+ .addOptionsModification(
+ options ->
+ options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
+ .enableNoVerticalClassMergingAnnotations())
.addVerticallyMergedClassesInspector(
inspector -> {
if (enableVerticalClassMerging) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/InstancePutToInterfaceWithObjectMergingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/InstancePutToInterfaceWithObjectMergingTest.java
index 980eebb..d294a77 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/InstancePutToInterfaceWithObjectMergingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/InstancePutToInterfaceWithObjectMergingTest.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoVerticalClassMerging;
-import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
@@ -56,7 +55,13 @@
.addKeepRules("-keepclassmembers class " + Main.class.getTypeName() + " { *** get(...); }")
.addNoVerticalClassMergingAnnotations()
.applyIf(
- !enableVerticalClassMerging, R8TestBuilder::enableNoVerticalClassMergingAnnotations)
+ !enableVerticalClassMerging,
+ testBuilder ->
+ testBuilder
+ .addOptionsModification(
+ options ->
+ options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
+ .enableNoVerticalClassMergingAnnotations())
.addVerticallyMergedClassesInspector(
inspector -> {
if (enableVerticalClassMerging) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ReturnObjectAsInterfaceMergingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ReturnObjectAsInterfaceMergingTest.java
index 48b234b..87d60b4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ReturnObjectAsInterfaceMergingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/ReturnObjectAsInterfaceMergingTest.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoVerticalClassMerging;
-import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
@@ -56,7 +55,13 @@
.addKeepRules("-keepclassmembers class " + Main.class.getTypeName() + " { *** get(...); }")
.addNoVerticalClassMergingAnnotations()
.applyIf(
- !enableVerticalClassMerging, R8TestBuilder::enableNoVerticalClassMergingAnnotations)
+ !enableVerticalClassMerging,
+ testBuilder ->
+ testBuilder
+ .addOptionsModification(
+ options ->
+ options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
+ .enableNoVerticalClassMergingAnnotations())
.addVerticallyMergedClassesInspector(
inspector -> {
if (enableVerticalClassMerging) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/StaticPutToInterfaceWithObjectMergingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/StaticPutToInterfaceWithObjectMergingTest.java
index bb948cbe..072b819 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/StaticPutToInterfaceWithObjectMergingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classmerger/vertical/StaticPutToInterfaceWithObjectMergingTest.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoVerticalClassMerging;
-import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
@@ -56,7 +55,13 @@
.addKeepRules("-keepclassmembers class " + Main.class.getTypeName() + " { *** get(...); }")
.addNoVerticalClassMergingAnnotations()
.applyIf(
- !enableVerticalClassMerging, R8TestBuilder::enableNoVerticalClassMergingAnnotations)
+ !enableVerticalClassMerging,
+ testBuilder ->
+ testBuilder
+ .addOptionsModification(
+ options ->
+ options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
+ .enableNoVerticalClassMergingAnnotations())
.addVerticallyMergedClassesInspector(
inspector -> {
if (enableVerticalClassMerging) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/StaticInvokeWithMultipleObjectsForInterfaceTypesTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/StaticInvokeWithMultipleObjectsForInterfaceTypesTest.java
index 0c9a135..4dc19d3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/StaticInvokeWithMultipleObjectsForInterfaceTypesTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/StaticInvokeWithMultipleObjectsForInterfaceTypesTest.java
@@ -65,7 +65,13 @@
.addNoVerticalClassMergingAnnotations()
.applyIf(!enableInlining, R8TestBuilder::enableInliningAnnotations)
.applyIf(
- !enableVerticalClassMerging, R8TestBuilder::enableNoVerticalClassMergingAnnotations)
+ !enableVerticalClassMerging,
+ testBuilder ->
+ testBuilder
+ .addOptionsModification(
+ options ->
+ options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
+ .enableNoVerticalClassMergingAnnotations())
.enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceCheckCastTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceCheckCastTest.java
index 01f306a..c756f8c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceCheckCastTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceCheckCastTest.java
@@ -41,7 +41,7 @@
.addProgramClasses(getProgramClasses())
.addProgramClassFileData(getTransformedMainClass())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
@Test
@@ -52,7 +52,7 @@
.addProgramClassFileData(getTransformedMainClass())
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
@Test
@@ -61,12 +61,14 @@
.addProgramClasses(getProgramClasses())
.addProgramClassFileData(getTransformedMainClass())
.addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
.enableInliningAnnotations()
// TODO(b/214496607): I should not be merged into A in the first place, since I is open.
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutputLines(true));
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
private List<Class<?>> getProgramClasses() {
@@ -100,11 +102,7 @@
.transform();
}
- private List<String> getExpectedOutputLines(boolean isR8) {
- if (isR8) {
- // TODO(b/214496607): R8 should not optimize the check-cast instruction since I is open.
- return ImmutableList.of("OK", "OK");
- }
+ private List<String> getExpectedOutputLines() {
if (parameters.isDexRuntime()
&& parameters
.getDexRuntimeVersion()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInliningTest.java
index 98db3cc..4eaa6a4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInliningTest.java
@@ -7,6 +7,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -39,7 +40,7 @@
.addProgramClasses(getProgramClasses())
.addProgramClassFileData(getTransformedMainClass())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
@Test
@@ -50,7 +51,7 @@
.addProgramClassFileData(getTransformedMainClass())
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
@Test
@@ -59,11 +60,18 @@
.addProgramClasses(getProgramClasses())
.addProgramClassFileData(getTransformedMainClass())
.addKeepClassAndMembersRules(Main.class)
+ .addOptionsModification(
+ options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
+ // TODO(b/214496607): A and B should strictly speaking not be merged since A implements the
+ // open interface I, and there is an invoke-interface instruction in the program to I.m(),
+ // which should succeed if the receiver is an A, but fail with an ICCE if the receiver is a
+ // B.
+ .enableNoHorizontalClassMergingAnnotations()
// TODO(b/214496607): I should not be merged into A in the first place, since I is open.
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutputLines(true));
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
private List<Class<?>> getProgramClasses() {
@@ -90,11 +98,7 @@
.transform();
}
- private List<String> getExpectedOutputLines(boolean isR8) {
- if (isR8) {
- // TODO(b/214496607): R8 should not inline the invoke instruction since I is open.
- return ImmutableList.of("A", "A");
- }
+ private List<String> getExpectedOutputLines() {
return ImmutableList.of("A", "ICCE");
}
@@ -124,6 +128,7 @@
void m();
}
+ @NoHorizontalClassMerging
static class A implements I {
@Override
@@ -132,6 +137,7 @@
}
}
+ @NoHorizontalClassMerging
static class B {
public void m() {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInstanceofTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInstanceofTest.java
index d73550c..dac3848 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInstanceofTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenInterfaceInstanceofTest.java
@@ -41,7 +41,7 @@
.addProgramClasses(getProgramClasses())
.addProgramClassFileData(getTransformedMainClass())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
@Test
@@ -52,7 +52,7 @@
.addProgramClassFileData(getTransformedMainClass())
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
@Test
@@ -61,12 +61,14 @@
.addProgramClasses(getProgramClasses())
.addProgramClassFileData(getTransformedMainClass())
.addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
.enableInliningAnnotations()
// TODO(b/214496607): I should not be merged into A in the first place, since I is open.
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutputLines(true));
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
private List<Class<?>> getProgramClasses() {
@@ -93,11 +95,7 @@
.transform();
}
- private List<String> getExpectedOutputLines(boolean isR8) {
- if (isR8) {
- // TODO(b/214496607): R8 should not optimize the instanceof instruction since I is open.
- return ImmutableList.of("true", "true");
- }
+ private List<String> getExpectedOutputLines() {
if (parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V7_0_0)) {
return ImmutableList.of("true", "true");
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceAlwaysNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceAlwaysNullTest.java
new file mode 100644
index 0000000..816da5a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceAlwaysNullTest.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2022, 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.interfaces;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class OpenUninstantiatedInterfaceAlwaysNullTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForRuntime(parameters)
+ .addProgramClasses(getProgramClasses())
+ .addProgramClassFileData(getTransformedMainClass())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8(parameters.getBackend())
+ .addProgramClasses(getProgramClasses())
+ .addProgramClassFileData(getTransformedMainClass())
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(getProgramClasses())
+ .addProgramClassFileData(getTransformedMainClass())
+ .addKeepClassAndMembersRules(Main.class)
+ .addOptionsModification(
+ options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
+ // TODO(b/214496607): I should not be merged into A in the first place, since I is open.
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
+ }
+
+ private List<Class<?>> getProgramClasses() {
+ return ImmutableList.of(I.class, A.class, B.class);
+ }
+
+ private byte[] getTransformedMainClass() throws IOException {
+ return transformer(Main.class)
+ .transformTypeInsnInMethod(
+ "getB",
+ (opcode, type, visitor) -> {
+ assertEquals(opcode, Opcodes.NEW);
+ assertEquals(type, binaryName(A.class));
+ visitor.visitTypeInsn(opcode, binaryName(B.class));
+ })
+ .transformMethodInsnInMethod(
+ "getB",
+ (opcode, owner, name, descriptor, isInterface, visitor) -> {
+ assertEquals(opcode, Opcodes.INVOKESPECIAL);
+ assertEquals(owner, binaryName(A.class));
+ assertEquals(name, "<init>");
+ visitor.visitMethodInsn(opcode, binaryName(B.class), name, descriptor, isInterface);
+ })
+ .transform();
+ }
+
+ private List<String> getExpectedOutputLines() {
+ return ImmutableList.of("false");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ I b = getB();
+ System.out.println(b == null);
+ }
+
+ static I getB() {
+ return new A(); // transformed into new B().
+ }
+ }
+
+ @NoVerticalClassMerging
+ interface I {}
+
+ static class A implements I {}
+
+ static class B {}
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java
index 7740192..606871f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/interfaces/OpenUninstantiatedInterfaceInstanceofTest.java
@@ -40,7 +40,7 @@
.addProgramClasses(getProgramClasses())
.addProgramClassFileData(getTransformedMainClass())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
@Test
@@ -51,7 +51,7 @@
.addProgramClassFileData(getTransformedMainClass())
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutputLines(false));
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
@Test
@@ -60,10 +60,12 @@
.addProgramClasses(getProgramClasses())
.addProgramClassFileData(getTransformedMainClass())
.addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(getExpectedOutputLines(true));
+ .assertSuccessWithOutputLines(getExpectedOutputLines());
}
private List<Class<?>> getProgramClasses() {
@@ -90,11 +92,7 @@
.transform();
}
- private List<String> getExpectedOutputLines(boolean isR8) {
- if (isR8) {
- // TODO(b/214496607): R8 should not optimize the instanceof instruction since I is open.
- return ImmutableList.of("true");
- }
+ private List<String> getExpectedOutputLines() {
if (parameters.isDexRuntime()) {
if (parameters.getDexRuntimeVersion().isEqualTo(Version.V7_0_0)
|| parameters.getDexRuntimeVersion().isEqualTo(Version.V13_MASTER)) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/lambda/LambdaMethodInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/lambda/LambdaMethodInliningTest.java
index a63addb..a3d1e6b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/lambda/LambdaMethodInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/lambda/LambdaMethodInliningTest.java
@@ -65,7 +65,7 @@
assertTrue(
testClassMethodSubject
.streamInstructions()
- .anyMatch(
+ .noneMatch(
instruction ->
instruction.isInvokeVirtual()
&& instruction.getMethod().toSourceString().contains("println")));
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/B146957343.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/B146957343.java
index 4ca57c1..82755bc 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/B146957343.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/B146957343.java
@@ -33,7 +33,7 @@
}
@Test
- public void testWithoutR8() throws Exception {
+ public void testRuntime() throws Exception {
testForRuntime(parameters)
.addProgramClasses(I.class, J.class, Main.class)
.addProgramClassFileData(getAimplementsI())
@@ -42,30 +42,15 @@
}
@Test
- public void testWithUninstantiatedTypeOptimizationForInterfaces() throws Exception {
+ public void testR8() throws Exception {
testForR8(parameters.getBackend())
.addProgramClasses(I.class, J.class, Main.class)
.addProgramClassFileData(getAimplementsI())
.addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
- .addOptionsModification(
- options -> options.enableUninstantiatedTypeOptimizationForInterfaces = true)
- .compile()
- .run(parameters.getRuntime(), Main.class)
- .assertFailureWithErrorThatThrows(NullPointerException.class);
- }
-
- @Test
- public void testWithoutUninstantiatedTypeOptimizationForInterfaces() throws Exception {
- testForR8(parameters.getBackend())
- .addProgramClasses(I.class, J.class, Main.class)
- .addProgramClassFileData(getAimplementsI())
- .addKeepMainRule(Main.class)
- .enableInliningAnnotations()
- .setMinApi(parameters.getApiLevel())
- .addOptionsModification(
- options -> options.enableUninstantiatedTypeOptimizationForInterfaces = false)
.compile()
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("In A.f()");
diff --git a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
index 488271b..c14b357 100644
--- a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -73,8 +73,9 @@
.count();
long paramNullCheckCount =
countCall(testMethod, "Intrinsics", "checkParameterIsNotNull");
- // One after Iterator#hasNext, and another in the filter predicate: sinceYear != null.
- assertEquals(2, ifzCount);
+ // TODO(b/214496607): Should be one after Iterator#hasNext, and another in the filter
+ // predicate: sinceYear != null.
+ assertEquals(testParameters.isCfRuntime() ? 5 : 2, ifzCount);
assertEquals(0, paramNullCheckCount);
});
}
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetExecutionTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetExecutionTest.java
index 4726fe4..1446e76 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleTargetExecutionTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetExecutionTest.java
@@ -84,10 +84,6 @@
}
private String getExpectedOutput() {
- String icceOrNot =
- enableInliningAnnotations || !parameters.canUseDefaultAndStaticInterfaceMethods()
- ? "ICCE"
- : "InterfaceWithDefault";
return StringUtils.lines(
"SubSubClassOne",
"SubSubClassOne",
@@ -97,7 +93,7 @@
"com.android.tools.r8.resolution.singletarget.one.AbstractSubClass",
"InterfaceWithDefault",
"InterfaceWithDefault",
- icceOrNot,
+ "ICCE",
"com.android.tools.r8.resolution.singletarget.one.SubSubClassTwo",
"com.android.tools.r8.resolution.singletarget.one.SubSubClassTwo",
"AbstractTopClass",
@@ -109,7 +105,7 @@
"InterfaceWithDefault",
"InterfaceWithDefault",
"InterfaceWithDefault",
- icceOrNot,
+ "ICCE",
"InterfaceWithDefault",
"com.android.tools.r8.resolution.singletarget.one.SubSubClassTwo",
"InterfaceWithDefault",
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
index 7d18aee..8c72e58 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
@@ -196,7 +196,7 @@
appInfo.definitionForProgramType(reference.holder).getProgramDefaultInitializer();
Assert.assertNotNull(appInfo.resolveMethodOnClass(reference).getSingleTarget());
DexEncodedMethod singleVirtualTarget =
- appInfo.lookupSingleVirtualTarget(reference, context, false);
+ appInfo.lookupSingleVirtualTarget(appView, reference, context, false);
if (singleTargetHolderOrNull == null) {
Assert.assertNull(singleVirtualTarget);
} else {
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java
index 846c994..ca541ea 100644
--- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.AsmTestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -89,22 +90,23 @@
public static byte[] DUMP = BDump.dump();
+ private static AppView<AppInfoWithLiveness> appView;
private static AppInfoWithLiveness appInfo;
@BeforeClass
public static void computeAppInfo() throws Exception {
- appInfo =
+ appView =
computeAppViewWithLiveness(
- buildClasses(CLASSES)
- .addClassProgramData(DUMP)
- .addLibraryFile(getMostRecentAndroidJar())
- .build(),
- Main.class)
- .appInfo();
+ buildClasses(CLASSES)
+ .addClassProgramData(DUMP)
+ .addLibraryFile(getMostRecentAndroidJar())
+ .build(),
+ Main.class);
+ appInfo = appView.appInfo();
}
private static DexMethod buildMethod(Class clazz, String name) {
- return buildNullaryVoidMethod(clazz, name, appInfo.dexItemFactory());
+ return buildNullaryVoidMethod(clazz, name, appView.dexItemFactory());
}
@Parameters(name = "{0}")
@@ -131,7 +133,7 @@
assertEquals(methodOnBReference, resolved.getReference());
assertFalse(resolutionResult.isVirtualTarget());
DexEncodedMethod singleVirtualTarget =
- appInfo.lookupSingleVirtualTarget(methodOnBReference, methodOnB, false);
+ appInfo.lookupSingleVirtualTarget(appView, methodOnBReference, methodOnB, false);
Assert.assertNull(singleVirtualTarget);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java
index ca29588..ac0cdef 100644
--- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -137,22 +138,23 @@
public static List<byte[]> DUMPS = ImmutableList.of(BaseDump.dump(), ADump.dump());
+ private static AppView<AppInfoWithLiveness> appView;
private static AppInfoWithLiveness appInfo;
@BeforeClass
public static void computeAppInfo() throws Exception {
- appInfo =
+ appView =
computeAppViewWithLiveness(
- buildClasses(CLASSES)
- .addClassProgramData(DUMPS)
- .addLibraryFile(getMostRecentAndroidJar())
- .build(),
- Main.class)
- .appInfo();
+ buildClasses(CLASSES)
+ .addClassProgramData(DUMPS)
+ .addLibraryFile(getMostRecentAndroidJar())
+ .build(),
+ Main.class);
+ appInfo = appView.appInfo();
}
private static DexMethod buildMethod(Class clazz, String name) {
- return buildNullaryVoidMethod(clazz, name, appInfo.dexItemFactory());
+ return buildNullaryVoidMethod(clazz, name, appView.dexItemFactory());
}
@Parameters(name = "{0}")
@@ -171,14 +173,15 @@
@Test
public void lookupSingleTarget() {
- DexProgramClass bClass = appInfo.definitionForProgramType(methodOnB.holder);
+ DexProgramClass bClass = appView.definitionForProgramType(methodOnB.holder);
MethodResolutionResult resolutionResult =
appInfo.resolveMethodOnClass(methodOnB, methodOnB.holder);
DexEncodedMethod resolved = resolutionResult.getSingleTarget();
assertEquals(methodOnA, resolved.getReference());
assertFalse(resolutionResult.isVirtualTarget());
DexEncodedMethod singleVirtualTarget =
- appInfo.lookupSingleVirtualTarget(methodOnB, bClass.getProgramDefaultInitializer(), false);
+ appInfo.lookupSingleVirtualTarget(
+ appView, methodOnB, bClass.getProgramDefaultInitializer(), false);
Assert.assertNull(singleVirtualTarget);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java b/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java
index a8bbdf4..7664322 100644
--- a/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java
@@ -22,7 +22,8 @@
import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.DynamicTypeWithLowerBound;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.Sets;
import java.util.Set;
@@ -61,10 +62,16 @@
ProgramMethod mainMethod =
appInfo.definitionForProgramType(typeMain).lookupProgramMethod(mainMethodReference);
DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
- ClassTypeElement latticeB =
- ClassTypeElement.create(typeB, Nullability.definitelyNotNull(), appView);
+ TypeElement latticeA = typeA.toTypeElement(appView);
+ ClassTypeElement latticeB = typeB.toTypeElement(appView).asClassType();
DexEncodedMethod singleTarget =
- appInfo.lookupSingleVirtualTarget(fooA, mainMethod, false, t -> false, typeA, latticeB);
+ appInfo.lookupSingleVirtualTarget(
+ appView,
+ fooA,
+ mainMethod,
+ false,
+ t -> false,
+ DynamicTypeWithLowerBound.create(appView, latticeA, latticeB));
assertNotNull(singleTarget);
DexMethod fooB = buildNullaryVoidMethod(B.class, "foo", appInfo.dexItemFactory());
assertEquals(fooB, singleTarget.getReference());
@@ -88,10 +95,16 @@
ProgramMethod mainMethod =
appInfo.definitionForProgramType(typeMain).lookupProgramMethod(mainMethodReference);
DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
- ClassTypeElement latticeB =
- ClassTypeElement.create(typeB, Nullability.definitelyNotNull(), appView);
+ TypeElement latticeA = typeA.toTypeElement(appView);
+ ClassTypeElement latticeB = typeB.toTypeElement(appView).asClassType();
DexEncodedMethod singleTarget =
- appInfo.lookupSingleVirtualTarget(fooA, mainMethod, false, t -> false, typeA, latticeB);
+ appInfo.lookupSingleVirtualTarget(
+ appView,
+ fooA,
+ mainMethod,
+ false,
+ t -> false,
+ DynamicTypeWithLowerBound.create(appView, latticeA, latticeB));
assertNotNull(singleTarget);
DexMethod fooB = buildNullaryVoidMethod(B.class, "foo", appInfo.dexItemFactory());
assertEquals(fooB, singleTarget.getReference());
@@ -138,10 +151,16 @@
assert false;
});
assertEquals(expected, actual);
- ClassTypeElement latticeC =
- ClassTypeElement.create(typeC, Nullability.definitelyNotNull(), appView);
+ TypeElement latticeA = typeA.toTypeElement(appView);
+ ClassTypeElement latticeC = typeC.toTypeElement(appView).asClassType();
DexEncodedMethod singleTarget =
- appInfo.lookupSingleVirtualTarget(fooA, mainMethod, false, t -> false, typeA, latticeC);
+ appInfo.lookupSingleVirtualTarget(
+ appView,
+ fooA,
+ mainMethod,
+ false,
+ t -> false,
+ DynamicTypeWithLowerBound.create(appView, latticeA, latticeC));
assertNull(singleTarget);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/singletarget/SuccessAndInvalidLookupTest.java b/src/test/java/com/android/tools/r8/resolution/singletarget/SuccessAndInvalidLookupTest.java
index 85bd2ca..ca2dee8 100644
--- a/src/test/java/com/android/tools/r8/resolution/singletarget/SuccessAndInvalidLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/singletarget/SuccessAndInvalidLookupTest.java
@@ -17,7 +17,11 @@
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.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
+import com.android.tools.r8.optimize.interfaces.collection.NonEmptyOpenClosedInterfacesCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.Collections;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -51,13 +55,17 @@
ProgramMethod mainMethod =
appInfo.definitionForProgramType(typeMain).lookupProgramMethod(mainMethodReference);
DexType typeA = buildType(A.class, appInfo.dexItemFactory());
+ DynamicType dynamicTypeA =
+ DynamicTypeWithUpperBound.create(appView, typeA.toTypeElement(appView));
DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
DexEncodedMethod singleTarget =
- appInfo.lookupSingleVirtualTarget(fooA, mainMethod, false, t -> false, typeA, null);
+ appInfo.lookupSingleVirtualTarget(
+ appView, fooA, mainMethod, false, t -> false, dynamicTypeA);
assertNotNull(singleTarget);
assertEquals(fooA, singleTarget.getReference());
DexEncodedMethod invalidSingleTarget =
- appInfo.lookupSingleVirtualTarget(fooA, mainMethod, true, t -> false, typeA, null);
+ appInfo.lookupSingleVirtualTarget(
+ appView, fooA, mainMethod, true, t -> false, dynamicTypeA);
assertNull(invalidSingleTarget);
}
@@ -70,6 +78,8 @@
.build(),
factory ->
buildConfigForRules(factory, buildKeepRuleForClassAndMethods(Main.class, factory)));
+ appView.setOpenClosedInterfacesCollection(
+ new NonEmptyOpenClosedInterfacesCollection(Collections.emptySet()));
AppInfoWithLiveness appInfo = appView.appInfo();
DexType typeMain = buildType(Main.class, appInfo.dexItemFactory());
DexMethod mainMethodReference =
@@ -77,14 +87,18 @@
ProgramMethod mainMethod =
appInfo.definitionForProgramType(typeMain).lookupProgramMethod(mainMethodReference);
DexType typeA = buildType(I.class, appInfo.dexItemFactory());
+ DynamicType dynamicTypeA =
+ DynamicTypeWithUpperBound.create(appView, typeA.toTypeElement(appView));
DexMethod fooI = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory());
DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
DexEncodedMethod singleTarget =
- appInfo.lookupSingleVirtualTarget(fooI, mainMethod, true, t -> false, typeA, null);
+ appInfo.lookupSingleVirtualTarget(
+ appView, fooI, mainMethod, true, t -> false, dynamicTypeA);
assertNotNull(singleTarget);
assertEquals(fooA, singleTarget.getReference());
DexEncodedMethod invalidSingleTarget =
- appInfo.lookupSingleVirtualTarget(fooI, mainMethod, false, t -> false, typeA, null);
+ appInfo.lookupSingleVirtualTarget(
+ appView, fooI, mainMethod, false, t -> false, dynamicTypeA);
assertNull(invalidSingleTarget);
}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java
index fb48ff4..84f5522 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java
@@ -32,10 +32,7 @@
RetraceApiUnknownJsonTest.ApiTest.class,
RetraceApiRewriteFrameInlineNpeTest.ApiTest.class,
RetraceApiAmbiguousOriginalRangeTest.ApiTest.class,
- RetraceApiOutsideLineRangeTest.ApiTest.class);
-
- public static List<Class<? extends RetraceApiBinaryTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
- ImmutableList.of(
+ RetraceApiOutsideLineRangeTest.ApiTest.class,
RetraceApiRewriteFrameInlineNpeResidualTest.ApiTest.class,
RetraceApiOutlineNoInlineTest.ApiTest.class,
RetraceApiOutlineInlineTest.ApiTest.class,
@@ -43,6 +40,9 @@
RetraceApiInlineInOutlineTest.ApiTest.class,
RetraceApiSingleFrameTest.ApiTest.class);
+ public static List<Class<? extends RetraceApiBinaryTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
+ ImmutableList.of();
+
private final TemporaryFolder temp;
public RetraceApiTestCollection(TemporaryFolder temp) {
diff --git a/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java b/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java
index e59bfb9..a9491f4 100644
--- a/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/JavaScriptScriptEngineTest.java
@@ -56,6 +56,14 @@
testForR8(parameters.getBackend())
.addInnerClasses(JavaScriptScriptEngineTest.class)
.addKeepMainRule(TestClass.class)
+ .applyIf(
+ parameters.isDexRuntime(),
+ testBuilder ->
+ testBuilder.addOptionsModification(
+ options ->
+ options
+ .getOpenClosedInterfacesOptions()
+ .suppressAllOpenInterfacesDueToMissingClasses()))
.setMinApi(parameters.getApiLevel())
.apply(
b -> {
diff --git a/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java b/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java
index dd55c22..7bb21a3 100644
--- a/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/ScriptEngineTest.java
@@ -56,6 +56,14 @@
testForR8(parameters.getBackend())
.addInnerClasses(ScriptEngineTest.class)
.addKeepMainRule(TestClass.class)
+ .applyIf(
+ parameters.isDexRuntime(),
+ testBuilder ->
+ testBuilder.addOptionsModification(
+ options ->
+ options
+ .getOpenClosedInterfacesOptions()
+ .suppressAllOpenInterfacesDueToMissingClasses()))
.setMinApi(parameters.getApiLevel())
.addDataEntryResources(
DataEntryResource.fromBytes(
diff --git a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
index f22ae69..9d3e55f 100644
--- a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
@@ -16,7 +16,6 @@
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRunResult;
-import com.android.tools.r8.TestRuntime;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
@@ -40,8 +39,7 @@
D8,
JAVAC,
PROGUARD,
- R8,
- R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES
+ R8
}
private enum Mode {
@@ -49,7 +47,7 @@
@Override
public String getExpectedOutput(
- Compiler compiler, TestRuntime runtime, boolean useInterface) {
+ Compiler compiler, TestParameters parameters, boolean useInterface) {
return StringUtils.joinLines("Hello!", "Goodbye!", "");
}
@@ -62,21 +60,37 @@
@Override
public String getExpectedOutput(
- Compiler compiler, TestRuntime runtime, boolean useInterface) {
+ Compiler compiler, TestParameters parameters, boolean useInterface) {
if (!useInterface) {
return StringUtils.joinLines("Hello!", "");
}
switch (compiler) {
- case D8:
+ case JAVAC:
+ return StringUtils.joinLines("Hello!", "Goodbye!", "");
+
case DX:
- switch (runtime.asDex().getVm().getVersion()) {
+ case D8:
+ case R8:
+ if (parameters.isCfRuntime()) {
+ assert compiler == Compiler.R8;
+ return StringUtils.joinLines("Hello!", "Goodbye!", "");
+ }
+ switch (parameters.getDexRuntimeVersion()) {
case V4_0_4:
case V4_4_4:
case V10_0_0:
case V12_0_0:
return StringUtils.joinLines("Hello!", "Goodbye!", "");
+ case V5_1_1:
+ case V6_0_1:
+ case V8_1_0:
+ case V9_0_0:
+ case DEFAULT:
+ return StringUtils.joinLines(
+ "Hello!", "Unexpected outcome of checkcast", "Goodbye!", "");
+
case V7_0_0:
case V13_MASTER:
return StringUtils.joinLines(
@@ -87,25 +101,13 @@
"");
default:
- // Fallthrough.
+ throw new Unreachable();
}
- case R8:
case PROGUARD:
return StringUtils.joinLines(
"Hello!", "Unexpected outcome of checkcast", "Goodbye!", "");
- case R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES:
- return StringUtils.joinLines(
- "Hello!",
- "Unexpected outcome of getstatic",
- "Unexpected outcome of checkcast",
- "Goodbye!",
- "");
-
- case JAVAC:
- return StringUtils.joinLines("Hello!", "Goodbye!", "");
-
default:
throw new Unreachable();
}
@@ -120,14 +122,13 @@
@Override
public String getExpectedOutput(
- Compiler compiler, TestRuntime runtime, boolean useInterface) {
+ Compiler compiler, TestParameters parameters, boolean useInterface) {
if (useInterface) {
return StringUtils.joinLines("Hello!", "In verifiable method!", "Goodbye!", "");
}
switch (compiler) {
case R8:
- case R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES:
case PROGUARD:
// The unverifiable method has been removed as a result of tree shaking, so the code
// does not fail with a verification error when trying to load class UnverifiableClass.
@@ -147,7 +148,7 @@
};
public abstract String getExpectedOutput(
- Compiler compiler, TestRuntime runtime, boolean useInterface);
+ Compiler compiler, TestParameters parameters, boolean useInterface);
public abstract String instruction();
}
@@ -301,7 +302,10 @@
.addOptionsModification(
options -> {
if (mode == Mode.INVOKE_UNVERIFIABLE_METHOD) {
+ options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces();
options.testing.allowTypeErrors = true;
+ } else if (mode == Mode.INVOKE_VERIFIABLE_METHOD_ON_UNVERIFIABLE_CLASS) {
+ options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces();
}
})
.allowDiagnosticWarningMessages(allowDiagnosticWarningMessages)
@@ -316,36 +320,6 @@
+ " type check and will be assumed to be unreachable.")))
.run(parameters.getRuntime(), mainClass.name);
checkTestRunResult(r8Result, Compiler.R8);
-
- R8TestRunResult r8ResultWithUninstantiatedTypeOptimizationForInterfaces =
- testForR8(parameters.getBackend())
- .addProgramFiles(inputJar)
- .addKeepMainRule(mainClass.name)
- .addKeepRules(
- "-keep class TestClass { public static I g; }",
- "-neverinline class TestClass { public static void m(); }")
- .enableProguardTestOptions()
- .addOptionsModification(
- options -> {
- if (mode == Mode.INVOKE_UNVERIFIABLE_METHOD) {
- options.testing.allowTypeErrors = true;
- }
- options.enableUninstantiatedTypeOptimizationForInterfaces = true;
- })
- .allowDiagnosticWarningMessages(allowDiagnosticWarningMessages)
- .setMinApi(parameters.getApiLevel())
- .compile()
- .applyIf(
- allowDiagnosticWarningMessages,
- result ->
- result.assertAllWarningMessagesMatch(
- equalTo(
- "The method `void UnverifiableClass.unverifiableMethod()` does not"
- + " type check and will be assumed to be unreachable.")))
- .run(parameters.getRuntime(), mainClass.name);
- checkTestRunResult(
- r8ResultWithUninstantiatedTypeOptimizationForInterfaces,
- Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES);
}
private void checkTestRunResult(TestRunResult<?> result, Compiler compiler) {
@@ -359,7 +333,6 @@
result.assertSuccessWithOutput(getExpectedOutput(compiler));
} else {
if (compiler == Compiler.R8
- || compiler == Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES
|| compiler == Compiler.PROGUARD) {
result.assertSuccessWithOutput(getExpectedOutput(compiler));
} else {
@@ -374,8 +347,7 @@
if (useInterface) {
result.assertSuccessWithOutput(getExpectedOutput(compiler));
} else {
- if (compiler == Compiler.R8
- || compiler == Compiler.R8_ENABLE_UNININSTANTATED_TYPE_OPTIMIZATION_FOR_INTERFACES) {
+ if (compiler == Compiler.R8) {
result
.assertFailureWithOutput(getExpectedOutput(compiler))
.assertFailureWithErrorThatMatches(
@@ -396,7 +368,7 @@
}
private String getExpectedOutput(Compiler compiler) {
- return mode.getExpectedOutput(compiler, parameters.getRuntime(), useInterface);
+ return mode.getExpectedOutput(compiler, parameters, useInterface);
}
private Matcher<String> getMatcherForExpectedError() {
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 f53c113..f85290d 100644
--- a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
@@ -212,6 +212,11 @@
.addKeepMainRule(R8.class)
.addKeepClassRules(CLASS_WITH_ANNOTATED_METHOD)
.addKeepRules("-keepclassmembers class * { @" + PRESENT_ANNOTATION + " *** *(...); }")
+ .addOptionsModification(
+ options ->
+ options
+ .getOpenClosedInterfacesOptions()
+ .suppressZipFileAssignmentsToJavaLangAutoCloseable())
.addDontWarnGoogle()
.addDontWarnJavaxNullableAnnotation()
.apply(this::configureHorizontalClassMerging)
@@ -231,6 +236,11 @@
+ " *** *(...); }")
.addDontWarnGoogle()
.addDontWarnJavaxNullableAnnotation()
+ .addOptionsModification(
+ options ->
+ options
+ .getOpenClosedInterfacesOptions()
+ .suppressZipFileAssignmentsToJavaLangAutoCloseable())
.apply(this::configureHorizontalClassMerging)
.compile()
.graphInspector();
@@ -249,6 +259,11 @@
+ " *** *(...); }")
.addDontWarnGoogle()
.addDontWarnJavaxNullableAnnotation()
+ .addOptionsModification(
+ options ->
+ options
+ .getOpenClosedInterfacesOptions()
+ .suppressZipFileAssignmentsToJavaLangAutoCloseable())
.apply(this::configureHorizontalClassMerging)
.compile()
.graphInspector();
@@ -269,6 +284,11 @@
+ " <2> <3>(...); }")
.addDontWarnGoogle()
.addDontWarnJavaxNullableAnnotation()
+ .addOptionsModification(
+ options ->
+ options
+ .getOpenClosedInterfacesOptions()
+ .suppressZipFileAssignmentsToJavaLangAutoCloseable())
.apply(this::configureHorizontalClassMerging)
.compile()
.graphInspector();
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index a296b9b..6c05cb7 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -183,7 +183,7 @@
public void resetAllowTestOptions() {
handler = new KeepingDiagnosticHandler();
reporter = new Reporter(handler);
- parser = new ProguardConfigurationParser(new DexItemFactory(), reporter, true);
+ parser = new ProguardConfigurationParser(new DexItemFactory(), reporter, null, true);
}
@Test
@@ -992,7 +992,7 @@
@Test
public void parseKeepdirectories() {
ProguardConfigurationParser parser =
- new ProguardConfigurationParser(new DexItemFactory(), reporter, false);
+ new ProguardConfigurationParser(new DexItemFactory(), reporter, null, false);
parser.parse(Paths.get(KEEPDIRECTORIES));
verifyParserEndsCleanly();
}
diff --git a/src/test/java/com/android/tools/r8/shaking/RemoveCallToStaticInitTest.java b/src/test/java/com/android/tools/r8/shaking/RemoveCallToStaticInitTest.java
index 0d1fb3d..27c98f6 100644
--- a/src/test/java/com/android/tools/r8/shaking/RemoveCallToStaticInitTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/RemoveCallToStaticInitTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.shaking;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverInline;
@@ -51,14 +50,12 @@
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.run(parameters.getRuntime(), Main.class)
- // TODO(b/220667525): R8 should emit EXPECTED
- .assertSuccessWithOutputLines(R8_EXPECTED)
+ .assertSuccessWithOutputLines(EXPECTED)
.inspect(
inspector -> {
ClassSubject clazz = inspector.clazz(B.class);
assertThat(clazz, isPresent());
- // TODO(b/220667525): Should not remove bridge due to class init.
- assertThat(clazz.uniqueMethodWithName("foo"), not(isPresent()));
+ assertThat(clazz.uniqueMethodWithName("foo"), isPresent());
});
}
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
index 94bdbf1..82d8fff 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/WhyAreYouKeepingAllTest.java
@@ -47,6 +47,11 @@
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
.addKeepRuleFiles(MAIN_KEEP)
.addKeepRules(WHY_ARE_YOU_KEEPING_ALL)
+ .addOptionsModification(
+ options ->
+ options
+ .getOpenClosedInterfacesOptions()
+ .suppressZipFileAssignmentsToJavaLangAutoCloseable())
.collectStdout()
.compile()
.assertStdoutThatMatches(containsString("referenced in keep rule"))
diff --git a/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java b/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java
index 6f868fc..1464e82 100644
--- a/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/proxy/MockitoTest.java
@@ -5,7 +5,6 @@
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;
@@ -58,8 +57,11 @@
.inspector();
ClassSubject itf = inspector.clazz(M_I);
assertThat(itf, isPresent());
+ // TODO(b/214496607): This could be removed as a result of devirtualization, but this only
+ // happens if the call site is reprocessed, since we don't have knowledge of open/closed
+ // interfaces until the second optimization pass.
MethodSubject mtd = itf.uniqueMethodWithName("onEnterForeground");
- assertThat(mtd, not(isPresent()));
+ assertThat(mtd, isPresent());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java b/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
index 047a109..c850ae8 100644
--- a/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
@@ -139,8 +139,8 @@
private void noInterfaceKept(CodeInspector inspector) {
// Indirectly assert that method is inlined into x, y and z and that redundant field loads
// remove invokes.
- assertEquals(0, countInstructionInX(inspector, InstructionSubject::isInvokeInterface));
- assertEquals(0, countInstructionInY(inspector, InstructionSubject::isInvokeInterface));
+ assertEquals(3, countInstructionInX(inspector, InstructionSubject::isInvokeInterface));
+ assertEquals(3, countInstructionInY(inspector, InstructionSubject::isInvokeInterface));
assertEquals(0, countInstructionInZ(inspector, InstructionSubject::isInvokeVirtual));
}
@@ -149,7 +149,10 @@
runTest(
ImmutableList.of(),
this::noInterfaceKept,
- "TestClass 1\nTestClass 1\nTestClass 1\nEXCEPTION\n");
+ "TestClass 1\nTestClass 1\nTestClass 1\nProxy\nProxy\nProxy\n"
+ + "TestClass 2\nTestClass 2\nTestClass 2\nProxy\nProxy\nProxy\n"
+ + "TestClass 3\nTestClass 3\nTestClass 3\n"
+ + "TestClass 4\nTestClass 4\nTestClass 4\nSUCCESS\n");
}
private void baseInterfaceKept(CodeInspector inspector) {
@@ -157,7 +160,7 @@
assertEquals(3, countInstructionInX(inspector, InstructionSubject::isInvokeInterface));
// Indirectly assert that method is inlined into y and z and that redundant field loads
// remove invokes.
- assertEquals(0, countInstructionInY(inspector, InstructionSubject::isInvokeInterface));
+ assertEquals(3, countInstructionInY(inspector, InstructionSubject::isInvokeInterface));
assertEquals(0, countInstructionInZ(inspector, InstructionSubject::isInvokeVirtual));
assertEquals(0, countInstructionInZSubClass(inspector, InstructionSubject::isInvokeVirtual));
}
@@ -171,7 +174,9 @@
"}"),
this::baseInterfaceKept,
"TestClass 1\nTestClass 1\nTestClass 1\nProxy\nProxy\nProxy\n"
- + "TestClass 2\nTestClass 2\nTestClass 2\nEXCEPTION\n");
+ + "TestClass 2\nTestClass 2\nTestClass 2\nProxy\nProxy\nProxy\n"
+ + "TestClass 3\nTestClass 3\nTestClass 3\n"
+ + "TestClass 4\nTestClass 4\nTestClass 4\nSUCCESS\n");
}
private void subInterfaceKept(CodeInspector inspector) {
diff --git a/third_party/retrace/binary_compatibility.tar.gz.sha1 b/third_party/retrace/binary_compatibility.tar.gz.sha1
index ee23c0c..26b5e24 100644
--- a/third_party/retrace/binary_compatibility.tar.gz.sha1
+++ b/third_party/retrace/binary_compatibility.tar.gz.sha1
@@ -1 +1 @@
-40f612b228f0520a11a2e2f8745400163c70c82f
\ No newline at end of file
+3238b42ace7a8e81abf0336c069645c5b97dd470
\ No newline at end of file
diff --git a/tools/archive_desugar_jdk_libs.py b/tools/archive_desugar_jdk_libs.py
index 3a9cfd6..cc9bee4 100755
--- a/tools/archive_desugar_jdk_libs.py
+++ b/tools/archive_desugar_jdk_libs.py
@@ -106,7 +106,7 @@
bazel,
'--bazelrc=/dev/null',
'build',
- '--sandbox_debug',
+ '--spawn_strategy=local',
'--verbose_failures',
'maven_release' + ('_jdk11' if variant == 'jdk11' else '')]
utils.PrintCmd(cmd)