Merge commit '871f85e8' into dev-release
diff --git a/.gitignore b/.gitignore
index 6e4c0e3..bbe6eea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -138,10 +138,43 @@
third_party/opensource-apps/friendlyeats.tar.gz
third_party/opensource-apps/iosched
third_party/opensource-apps/iosched.tar.gz
+third_party/opensource-apps/kiss
+third_party/opensource-apps/kiss.tar.gz
+third_party/opensource-apps/materialistic
+third_party/opensource-apps/materialistic.tar.gz
+third_party/opensource-apps/minimal-todo
+third_party/opensource-apps/minimal-todo.tar.gz
+third_party/opensource-apps/muzei
+third_party/opensource-apps/muzei.tar.gz
+third_party/opensource-apps/newpipe
+third_party/opensource-apps/newpipe.tar.gz
+third_party/opensource-apps/rover-android
+third_party/opensource-apps/rover-android.tar.gz
+third_party/opensource-apps/santa-tracker
+third_party/opensource-apps/santa-tracker.tar.gz
+third_party/opensource-apps/signal-android
+third_party/opensource-apps/signal-android.tar.gz
+third_party/opensource-apps/simple-calendar
+third_party/opensource-apps/simple-calendar.tar.gz
+third_party/opensource-apps/simple-camera
+third_party/opensource-apps/simple-camera.tar.gz
+third_party/opensource-apps/simple-file-manager
+third_party/opensource-apps/simple-file-manager.tar.gz
+third_party/opensource-apps/simple-gallery
+third_party/opensource-apps/simple-gallery.tar.gz
+third_party/opensource-apps/sqldelight
+third_party/opensource-apps/sqldelight.tar.gz
third_party/opensource-apps/sunflower
third_party/opensource-apps/sunflower.tar.gz
+third_party/opensource-apps/tachiyomi
+third_party/opensource-apps/tachiyomi.tar.gz
+third_party/opensource-apps/tivi
+third_party/opensource-apps/tivi.tar.gz
+third_party/opensource-apps/tusky
+third_party/opensource-apps/tusky.tar.gz
third_party/opensource-apps/wikipedia
third_party/opensource-apps/wikipedia.tar.gz
+third_party/opensource-apps/android/compose-samples/*
third_party/opensource_apps
third_party/opensource_apps.tar.gz
third_party/proguard/*
diff --git a/build.gradle b/build.gradle
index 558919e..981dcfd 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1875,10 +1875,11 @@
}
def printStackTrace(TestResult result) {
+ filterStackTraces(result)
if (project.hasProperty('r8lib') || project.hasProperty('r8lib_no_deps')) {
def out = new StringBuffer()
def err = new StringBuffer()
- def command = "python tools/retrace.py"
+ def command = "python tools/retrace.py --quiet"
def header = "RETRACED STACKTRACE";
if (System.getenv('BUILDBOT_BUILDERNAME') != null
&& !System.getenv('BUILDBOT_BUILDERNAME').endsWith("_release")) {
@@ -1893,19 +1894,49 @@
result.exception.printStackTrace(processIn)
processIn.flush()
processIn.close()
- if (process.waitFor() != 0) {
+ def errorDuringRetracing = process.waitFor() != 0
+ if (errorDuringRetracing) {
out.append("ERROR DURING RETRACING\n")
out.append(err.toString())
}
- out.append("\n\n--------------------------------------\n")
- out.append("OBFUSCATED STACKTRACE\n")
- out.append("--------------------------------------\n")
- result.exceptions.add(0, new Exception(out.toString()))
+ if (project.hasProperty('print_obfuscated_stacktraces') || errorDuringRetracing) {
+ out.append("\n\n--------------------------------------\n")
+ out.append("OBFUSCATED STACKTRACE\n")
+ out.append("--------------------------------------\n")
+ } else {
+ result.exceptions.clear()
+ }
+ def exception = new Exception(out.toString())
+ exception.setStackTrace([] as StackTraceElement[])
+ result.exceptions.add(0, exception)
} else {
result.exception.printStackTrace()
}
}
+def filterStackTraces(TestResult result) {
+ for (Throwable throwable : result.getExceptions()) {
+ filterStackTrace(throwable)
+ }
+}
+
+def filterStackTrace(Throwable exception) {
+ if (!project.hasProperty('print_full_stacktraces')) {
+ def elements = []
+ def skipped = []
+ for (StackTraceElement element : exception.getStackTrace()) {
+ if (element.toString().contains("com.android.tools.r8")) {
+ elements.addAll(skipped)
+ elements.add(element)
+ skipped.clear()
+ } else {
+ skipped.add(element)
+ }
+ }
+ exception.setStackTrace(elements as StackTraceElement[])
+ }
+}
+
test {
if (project.hasProperty('generate_golden_files_to')) {
systemProperty 'generate_golden_files_to', project.property('generate_golden_files_to')
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index c0222e8..3ebf03f 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -167,15 +167,6 @@
}
}
builders {
- name: "linux-jdk8_9"
- mixins: "linux"
- mixins: "normal"
- priority: 26
- recipe {
- properties_j: "test_options:[\"--runtimes=jdk8:jdk9\", \"--no_internal\", \"--one_line_per_test\", \"--archive_failures\"]"
- }
- }
- builders {
name: "linux_release"
mixins: "normal"
mixins: "linux"
@@ -346,24 +337,6 @@
}
}
builders {
- name: "linux-run-on-as-app"
- mixins: "linux"
- mixins: "normal"
- recipe {
- properties: "run_on_apps:True"
- properties: "recompilation:False"
- }
- }
- builders {
- name: "linux-run-on-as-app-recompilation"
- mixins: "linux"
- mixins: "normal"
- recipe {
- properties: "run_on_apps:True"
- properties: "recompilation:True"
- }
- }
- builders {
name: "linux-run-on-app-dump"
mixins: "linux"
mixins: "normal"
@@ -373,15 +346,6 @@
}
}
builders {
- name: "linux-run-on-as-app_release"
- mixins: "linux"
- mixins: "normal"
- execution_timeout_secs: 25200 # 7h
- recipe {
- properties: "run_on_apps:True"
- }
- }
- builders {
name: "linux-run-on-app-dump_release"
mixins: "linux"
mixins: "normal"
@@ -448,5 +412,15 @@
properties: "tool:r8"
}
}
+ builders {
+ name: "kotlin-builder"
+ mixins: "linux"
+ mixins: "normal"
+ recipe {
+ properties_j: "test_options:[\"--not_used\"]"
+ properties: "test_wrapper:google-scripts/build.py"
+ properties: "kotlin_repo:True"
+ }
+ }
}
}
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index e76751b..1c2c87f 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -76,16 +76,6 @@
short_name: "internal"
}
builders {
- name: "buildbucket/luci.r8.ci/linux-run-on-as-app"
- category: "R8"
- short_name: "apps"
- }
- builders {
- name: "buildbucket/luci.r8.ci/linux-run-on-as-app-recompilation"
- category: "R8"
- short_name: "apps-rec"
- }
- builders {
name: "buildbucket/luci.r8.ci/linux-run-on-app-dump"
category: "R8"
short_name: "apps-dump"
@@ -166,11 +156,6 @@
short_name: "internal"
}
builders {
- name: "buildbucket/luci.r8.ci/linux-run-on-as-app_release"
- category: "R8 release"
- short_name: "apps"
- }
- builders {
name: "buildbucket/luci.r8.ci/linux-run-on-app-dump_release"
category: "R8 release"
short_name: "apps-dump"
@@ -190,5 +175,9 @@
category: "win release"
short_name: "win"
}
-
+ builders {
+ name: "buildbucket/luci.r8.ci/kotlin-builder"
+ category: "kotlin"
+ short_name: "kotlin_builder"
+ }
}
diff --git a/infra/config/global/luci-notify.cfg b/infra/config/global/luci-notify.cfg
index 1e48f29..92b76f3 100644
--- a/infra/config/global/luci-notify.cfg
+++ b/infra/config/global/luci-notify.cfg
@@ -39,11 +39,6 @@
repository: "https://r8.googlesource.com/r8"
}
builders {
- name: "linux-jdk8_9"
- bucket: "ci"
- repository: "https://r8.googlesource.com/r8"
- }
- builders {
name: "linux_release"
bucket: "ci"
repository: "https://r8.googlesource.com/r8"
@@ -139,26 +134,11 @@
repository: "https://r8.googlesource.com/r8"
}
builders {
- name: "linux-run-on-as-app"
- bucket: "ci"
- repository: "https://r8.googlesource.com/r8"
- }
- builders {
- name: "linux-run-on-as-app-recompilation"
- bucket: "ci"
- repository: "https://r8.googlesource.com/r8"
- }
- builders {
name: "linux-run-on-app-dump"
bucket: "ci"
repository: "https://r8.googlesource.com/r8"
}
builders {
- name: "linux-run-on-as-app_release"
- bucket: "ci"
- repository: "https://r8.googlesource.com/r8"
- }
- builders {
name: "linux-run-on-app-dump_release"
bucket: "ci"
repository: "https://r8.googlesource.com/r8"
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index a0b3fa3..a18a124 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -29,7 +29,6 @@
triggers: "linux"
triggers: "linux-jdk8"
triggers: "linux-jdk9"
- triggers: "linux-jdk8_9"
triggers: "linux-android-4.0.4"
triggers: "linux-android-4.4.4"
triggers: "linux-android-5.1.1"
@@ -38,8 +37,6 @@
triggers: "linux-android-8.1.0"
triggers: "linux-android-9.0.0"
triggers: "linux-android-10.0.0"
- triggers: "linux-run-on-as-app"
- triggers: "linux-run-on-as-app-recompilation"
triggers: "linux-run-on-app-dump"
triggers: "linux-internal"
triggers: "linux-jctf"
@@ -58,6 +55,16 @@
}
trigger {
+ id: "kotlin_trigger"
+ acl_sets: "default"
+ gitiles: {
+ repo: "https://github.googlesource.com/google/kotlin"
+ refs: "refs/heads/google-ir"
+ }
+ triggers: "kotlin-builder"
+}
+
+trigger {
id: "branch-gitiles-trigger"
acl_sets: "default"
gitiles: {
@@ -77,7 +84,6 @@
triggers: "linux-android-10.0.0_release"
triggers: "linux-internal_release"
triggers: "linux-jctf_release"
- triggers: "linux-run-on-as-app_release"
triggers: "linux-run-on-app-dump_release"
triggers: "linux_release"
triggers: "r8cf-linux-jctf_release"
@@ -128,6 +134,19 @@
}
job {
+ id: "kotlin-builder"
+ acl_sets: "default"
+ triggering_policy: {
+ max_concurrent_invocations: 1
+ }
+ buildbucket {
+ server: "cr-buildbucket.appspot.com"
+ bucket: "luci.r8.ci"
+ builder: "kotlin-builder"
+ }
+}
+
+job {
id: "linux"
acl_sets: "default"
triggering_policy: {
@@ -170,20 +189,6 @@
}
job {
- id: "linux-jdk8_9"
- acl_sets: "default"
- triggering_policy: {
- kind: GREEDY_BATCHING
- max_concurrent_invocations: 2
- }
- buildbucket {
- server: "cr-buildbucket.appspot.com"
- bucket: "luci.r8.ci"
- builder: "linux-jdk8_9"
- }
-}
-
-job {
id: "linux-android-4.0.4"
acl_sets: "default"
triggering_policy: {
@@ -429,35 +434,6 @@
}
job {
- id: "linux-run-on-as-app"
- acl_sets: "default"
- triggering_policy: {
- kind: GREEDY_BATCHING
- max_concurrent_invocations: 3
- }
- buildbucket {
- server: "cr-buildbucket.appspot.com"
- bucket: "luci.r8.ci"
- builder: "linux-run-on-as-app"
- }
-}
-
-job {
- id: "linux-run-on-as-app-recompilation"
- acl_sets: "default"
- triggering_policy: {
- kind: GREEDY_BATCHING
- max_concurrent_invocations: 3
- }
- buildbucket {
- server: "cr-buildbucket.appspot.com"
- bucket: "luci.r8.ci"
- builder: "linux-run-on-as-app-recompilation"
- }
-}
-
-
-job {
id: "linux-run-on-app-dump"
acl_sets: "default"
triggering_policy: {
@@ -472,20 +448,6 @@
}
job {
- id: "linux-run-on-as-app_release"
- acl_sets: "default"
- triggering_policy: {
- max_batch_size: 1
- max_concurrent_invocations: 3
- }
- buildbucket {
- server: "cr-buildbucket.appspot.com"
- bucket: "luci.r8.ci"
- builder: "linux-run-on-as-app_release"
- }
-}
-
-job {
id: "linux-run-on-app-dump_release"
acl_sets: "default"
triggering_policy: {
diff --git a/src/library_desugar/desugar_jdk_libs.json b/src/library_desugar/desugar_jdk_libs.json
index 98ceb80..ed216b0 100644
--- a/src/library_desugar/desugar_jdk_libs.json
+++ b/src/library_desugar/desugar_jdk_libs.json
@@ -2,7 +2,7 @@
"configuration_format_version": 3,
"group_id" : "com.tools.android",
"artifact_id" : "desugar_jdk_libs",
- "version": "1.1.0",
+ "version": "1.1.1",
"required_compilation_api_level": 26,
"synthesized_library_classes_package_prefix": "j$.",
"support_all_callbacks_from_library": true,
@@ -243,7 +243,7 @@
"-keepclassmembers class j$.util.concurrent.ConcurrentHashMap$TreeBin { int lockState; }",
"-keepclassmembers class j$.util.concurrent.ConcurrentHashMap { int sizeCtl; int transferIndex; long baseCount; int cellsBusy; }",
"-keepclassmembers class j$.util.concurrent.ConcurrentHashMap$CounterCell { long value; }",
- "-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); }",
+ "-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); public static final !synthetic <fields>; }",
"-keeppackagenames j$",
"-keepclassmembers class j$.util.IntSummaryStatistics { long count; long sum; int min; int max; }",
"-keepclassmembers class j$.util.LongSummaryStatistics { long count; long sum; long min; long max; }",
diff --git a/src/library_desugar/desugar_jdk_libs_alternative_3.json b/src/library_desugar/desugar_jdk_libs_alternative_3.json
index 628fc53..012d6ce 100644
--- a/src/library_desugar/desugar_jdk_libs_alternative_3.json
+++ b/src/library_desugar/desugar_jdk_libs_alternative_3.json
@@ -2,7 +2,7 @@
"configuration_format_version": 3,
"group_id" : "com.tools.android",
"artifact_id" : "desugar_jdk_libs_alternative_3",
- "version": "1.1.0",
+ "version": "1.1.1",
"required_compilation_api_level": 26,
"synthesized_library_classes_package_prefix": "j$.",
"support_all_callbacks_from_library": false,
@@ -246,7 +246,7 @@
"-keepclassmembers class j$.util.concurrent.ConcurrentHashMap$TreeBin { int lockState; }",
"-keepclassmembers class j$.util.concurrent.ConcurrentHashMap { int sizeCtl; int transferIndex; long baseCount; int cellsBusy; }",
"-keepclassmembers class j$.util.concurrent.ConcurrentHashMap$CounterCell { long value; }",
- "-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); }",
+ "-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); public static final !synthetic <fields>; }",
"-keeppackagenames j$",
"-keepclassmembers class j$.util.IntSummaryStatistics { long count; long sum; int min; int max; }",
"-keepclassmembers class j$.util.LongSummaryStatistics { long count; long sum; long min; long max; }",
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
index 036dd57..893bc64 100644
--- a/src/main/java/com/android/tools/r8/BaseCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -275,14 +275,16 @@
* @see #addMainDexListFiles(Path...)
*/
public B addMainDexListFiles(Collection<Path> files) {
- guard(() -> {
- try {
- app.addMainDexListFiles(files);
- } catch (NoSuchFileException e) {
- reporter.error(new StringDiagnostic(
- "Main-dex-ist file does not exist", new PathOrigin(Paths.get(e.getFile()))));
- }
- });
+ guard(
+ () -> {
+ try {
+ app.addMainDexListFiles(files);
+ } catch (NoSuchFileException e) {
+ reporter.error(
+ new StringDiagnostic(
+ "Main-dex-list file does not exist", new PathOrigin(Paths.get(e.getFile()))));
+ }
+ });
return self();
}
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 80dcf13..b12b467 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -109,6 +109,15 @@
return minApiLevel;
}
+ void dumpBaseCommandOptions(DumpOptions.Builder builder) {
+ builder
+ .setCompilationMode(getMode())
+ .setMinApi(getMinApiLevel())
+ .setOptimizeMultidexForLinearAlloc(isOptimizeMultidexForLinearAlloc())
+ .setThreadCount(getThreadCount())
+ .setDesugarState(getDesugarState());
+ }
+
/**
* Get the program consumer that will receive the compilation output.
*
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 3372bc9..501d4ae 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.utils.InternalOptions.DETERMINISTIC_DEBUGGING;
import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
+import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.inspector.Inspector;
@@ -449,7 +450,17 @@
internal.dumpInputToDirectory = null;
internal.dumpInputToFile = null;
}
+ internal.dumpOptions = dumpOptions();
return internal;
}
+
+ private DumpOptions dumpOptions() {
+ DumpOptions.Builder builder = DumpOptions.builder(Tool.D8);
+ dumpBaseCommandOptions(builder);
+ return builder
+ .setIntermediate(intermediate)
+ .setDesugaredLibraryConfiguration(libraryConfiguration)
+ .build();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/DumpOptions.java b/src/main/java/com/android/tools/r8/DumpOptions.java
new file mode 100644
index 0000000..cd2ac79
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/DumpOptions.java
@@ -0,0 +1,263 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8;
+
+import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.features.FeatureSplitConfiguration;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.utils.InternalOptions.DesugarState;
+import com.android.tools.r8.utils.ThreadUtils;
+import java.util.Optional;
+
+@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
+public class DumpOptions {
+
+ // The following keys and values should not be changed to keep the dump utility backward
+ // compatible with previous versions. They are also used by the python script compileDump and
+ // the corresponding CompileDumpCompatR8 java class.
+ private static final String TOOL_KEY = "tool";
+ private static final String MODE_KEY = "mode";
+ private static final String DEBUG_MODE_VALUE = "debug";
+ private static final String RELEASE_MODE_VALUE = "release";
+ private static final String MIN_API_KEY = "min-api";
+ private static final String OPTIMIZE_MULTIDEX_FOR_LINEAR_ALLOC_KEY =
+ "optimize-multidex-for-linear-alloc";
+ private static final String THREAD_COUNT_KEY = "thread-count";
+ private static final String DESUGAR_STATE_KEY = "desugar-state";
+ private static final String INTERMEDIATE_KEY = "intermediate";
+ private static final String INCLUDE_DATA_RESOURCES_KEY = "include-data-resources";
+ private static final String TREE_SHAKING_KEY = "tree-shaking";
+ private static final String MINIFICATION_KEY = "minification";
+ private static final String FORCE_PROGUARD_COMPATIBILITY_KEY = "force-proguard-compatibility";
+
+ private final Tool tool;
+ private final CompilationMode compilationMode;
+ private final int minApi;
+ private final boolean optimizeMultidexForLinearAlloc;
+ private final int threadCount;
+ private final DesugarState desugarState;
+ private final Optional<Boolean> intermediate;
+ private final Optional<Boolean> includeDataResources;
+ private final Optional<Boolean> treeShaking;
+ private final Optional<Boolean> minification;
+ private final Optional<Boolean> forceProguardCompatibility;
+
+ // Dump if present.
+ private final DesugaredLibraryConfiguration desugaredLibraryConfiguration;
+ private final FeatureSplitConfiguration featureSplitConfiguration;
+ private final ProguardConfiguration proguardConfiguration;
+
+ // Reporting only.
+ private final boolean dumpInputToFile;
+
+ private DumpOptions(
+ Tool tool,
+ CompilationMode compilationMode,
+ int minAPI,
+ DesugaredLibraryConfiguration desugaredLibraryConfiguration,
+ boolean optimizeMultidexForLinearAlloc,
+ int threadCount,
+ DesugarState desugarState,
+ Optional<Boolean> intermediate,
+ Optional<Boolean> includeDataResources,
+ Optional<Boolean> treeShaking,
+ Optional<Boolean> minification,
+ Optional<Boolean> forceProguardCompatibility,
+ FeatureSplitConfiguration featureSplitConfiguration,
+ ProguardConfiguration proguardConfiguration,
+ boolean dumpInputToFile) {
+ this.tool = tool;
+ this.compilationMode = compilationMode;
+ this.minApi = minAPI;
+ this.desugaredLibraryConfiguration = desugaredLibraryConfiguration;
+ this.optimizeMultidexForLinearAlloc = optimizeMultidexForLinearAlloc;
+ this.threadCount = threadCount;
+ this.desugarState = desugarState;
+ this.intermediate = intermediate;
+ this.includeDataResources = includeDataResources;
+ this.treeShaking = treeShaking;
+ this.minification = minification;
+ this.forceProguardCompatibility = forceProguardCompatibility;
+ this.featureSplitConfiguration = featureSplitConfiguration;
+ this.proguardConfiguration = proguardConfiguration;
+ this.dumpInputToFile = dumpInputToFile;
+ }
+
+ public String dumpOptions() {
+ StringBuilder builder = new StringBuilder();
+ addDumpEntry(builder, TOOL_KEY, tool.name());
+ // We keep the following values for backward compatibility.
+ addDumpEntry(
+ builder,
+ MODE_KEY,
+ compilationMode == CompilationMode.DEBUG ? DEBUG_MODE_VALUE : RELEASE_MODE_VALUE);
+ addDumpEntry(builder, MIN_API_KEY, minApi);
+ addDumpEntry(builder, OPTIMIZE_MULTIDEX_FOR_LINEAR_ALLOC_KEY, optimizeMultidexForLinearAlloc);
+ if (threadCount != ThreadUtils.NOT_SPECIFIED) {
+ addDumpEntry(builder, THREAD_COUNT_KEY, threadCount);
+ }
+ addDumpEntry(builder, DESUGAR_STATE_KEY, desugarState);
+ addOptionalDumpEntry(builder, INTERMEDIATE_KEY, intermediate);
+ addOptionalDumpEntry(builder, INCLUDE_DATA_RESOURCES_KEY, includeDataResources);
+ addOptionalDumpEntry(builder, TREE_SHAKING_KEY, treeShaking);
+ addOptionalDumpEntry(builder, MINIFICATION_KEY, minification);
+ addOptionalDumpEntry(builder, FORCE_PROGUARD_COMPATIBILITY_KEY, forceProguardCompatibility);
+ return builder.toString();
+ }
+
+ private void addOptionalDumpEntry(StringBuilder builder, String key, Optional<?> optionalValue) {
+ optionalValue.ifPresent(bool -> addDumpEntry(builder, key, bool));
+ }
+
+ private void addDumpEntry(StringBuilder builder, String key, Object value) {
+ builder.append(key).append("=").append(value).append("\n");
+ }
+
+ private boolean hasDesugaredLibraryConfiguration() {
+ return desugaredLibraryConfiguration != null
+ && desugaredLibraryConfiguration
+ != DesugaredLibraryConfiguration.EMPTY_DESUGARED_LIBRARY_CONFIGURATION;
+ }
+
+ public String getDesugaredLibraryJsonSource() {
+ if (hasDesugaredLibraryConfiguration()) {
+ return desugaredLibraryConfiguration.getJsonSource();
+ }
+ return null;
+ }
+
+ public FeatureSplitConfiguration getFeatureSplitConfiguration() {
+ return featureSplitConfiguration;
+ }
+
+ public String getParsedProguardConfiguration() {
+ return proguardConfiguration == null ? null : proguardConfiguration.getParsedConfiguration();
+ }
+
+ public boolean dumpInputToFile() {
+ return dumpInputToFile;
+ }
+
+ public static Builder builder(Tool tool) {
+ return new Builder(tool);
+ }
+
+ public static class Builder {
+ private final Tool tool;
+ private CompilationMode compilationMode;
+ private int minApi;
+ private boolean optimizeMultidexForLinearAlloc;
+ private int threadCount;
+ private DesugarState desugarState;
+ private Optional<Boolean> intermediate = Optional.empty();
+ private Optional<Boolean> includeDataResources = Optional.empty();
+ private Optional<Boolean> treeShaking = Optional.empty();
+ private Optional<Boolean> minification = Optional.empty();
+ private Optional<Boolean> forceProguardCompatibility = Optional.empty();
+ // Dump if present.
+ private DesugaredLibraryConfiguration desugaredLibraryConfiguration;
+ private FeatureSplitConfiguration featureSplitConfiguration;
+ private ProguardConfiguration proguardConfiguration;
+
+ // Reporting only.
+ private boolean dumpInputToFile;
+
+ public Builder(Tool tool) {
+ this.tool = tool;
+ }
+
+ public Builder setCompilationMode(CompilationMode compilationMode) {
+ this.compilationMode = compilationMode;
+ return this;
+ }
+
+ public Builder setMinApi(int minAPI) {
+ this.minApi = minAPI;
+ return this;
+ }
+
+ public Builder setDesugaredLibraryConfiguration(
+ DesugaredLibraryConfiguration desugaredLibraryConfiguration) {
+ this.desugaredLibraryConfiguration = desugaredLibraryConfiguration;
+ return this;
+ }
+
+ public Builder setOptimizeMultidexForLinearAlloc(boolean optimizeMultidexForLinearAlloc) {
+ this.optimizeMultidexForLinearAlloc = optimizeMultidexForLinearAlloc;
+ return this;
+ }
+
+ public Builder setThreadCount(int threadCount) {
+ this.threadCount = threadCount;
+ return this;
+ }
+
+ public Builder setDesugarState(DesugarState desugarState) {
+ this.desugarState = desugarState;
+ return this;
+ }
+
+ public Builder setIntermediate(boolean intermediate) {
+ this.intermediate = Optional.of(intermediate);
+ return this;
+ }
+
+ public Builder setIncludeDataResources(Optional<Boolean> includeDataResources) {
+ this.includeDataResources = includeDataResources;
+ return this;
+ }
+
+ public Builder setForceProguardCompatibility(boolean forceProguardCompatibility) {
+ this.forceProguardCompatibility = Optional.of(forceProguardCompatibility);
+ return this;
+ }
+
+ public Builder setMinification(boolean minification) {
+ this.minification = Optional.of(minification);
+ return this;
+ }
+
+ public Builder setTreeShaking(boolean treeShaking) {
+ this.treeShaking = Optional.of(treeShaking);
+ return this;
+ }
+
+ public Builder setDumpInputToFile(boolean dumpInputToFile) {
+ this.dumpInputToFile = dumpInputToFile;
+ return this;
+ }
+
+ public Builder setFeatureSplitConfiguration(
+ FeatureSplitConfiguration featureSplitConfiguration) {
+ this.featureSplitConfiguration = featureSplitConfiguration;
+ return this;
+ }
+
+ public Builder setProguardConfiguration(ProguardConfiguration proguardConfiguration) {
+ this.proguardConfiguration = proguardConfiguration;
+ return this;
+ }
+
+ public DumpOptions build() {
+ return new DumpOptions(
+ tool,
+ compilationMode,
+ minApi,
+ desugaredLibraryConfiguration,
+ optimizeMultidexForLinearAlloc,
+ threadCount,
+ desugarState,
+ intermediate,
+ includeDataResources,
+ treeShaking,
+ minification,
+ forceProguardCompatibility,
+ featureSplitConfiguration,
+ proguardConfiguration,
+ dumpInputToFile);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 8e4bb2a..dce2b21 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -7,6 +7,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.errors.DexFileOverflowDiagnostic;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.inspector.Inspector;
@@ -196,6 +197,7 @@
assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
internal.threadCount = getThreadCount();
}
+ internal.dumpOptions = dumpOptions();
return internal;
}
@@ -383,4 +385,10 @@
@Override
public void finished(DiagnosticsHandler handler) {}
}
+
+ private DumpOptions dumpOptions() {
+ DumpOptions.Builder builder = DumpOptions.builder(Tool.L8);
+ dumpBaseCommandOptions(builder);
+ return builder.setDesugaredLibraryConfiguration(libraryConfiguration).build();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index b97eb7f..cfb3540 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -40,8 +40,11 @@
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.graph.analysis.ClassInitializerAssertionEnablingAnalysis;
import com.android.tools.r8.graph.analysis.InitializedClassesInInstanceMethodsAnalysis;
+import com.android.tools.r8.graph.classmerging.StaticallyMergedClasses;
+import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerGraphLens;
+import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
@@ -523,7 +526,11 @@
NestedGraphLens lens = staticClassMerger.run();
appView.rewriteWithLens(lens);
timing.end();
+ } else {
+ appView.setStaticallyMergedClasses(StaticallyMergedClasses.empty());
}
+ assert appView.staticallyMergedClasses() != null;
+
if (options.enableVerticalClassMerging) {
timing.begin("VerticalClassMerger");
VerticalClassMerger verticalClassMerger =
@@ -535,12 +542,14 @@
mainDexTracingResult);
VerticalClassMergerGraphLens lens = verticalClassMerger.run();
if (lens != null) {
- appView.setVerticallyMergedClasses(lens.getMergedClasses());
appView.rewriteWithLens(lens);
runtimeTypeCheckInfo = runtimeTypeCheckInfo.rewriteWithLens(lens);
}
timing.end();
+ } else {
+ appView.setVerticallyMergedClasses(VerticallyMergedClasses.empty());
}
+ assert appView.verticallyMergedClasses() != null;
if (options.enableArgumentRemoval) {
SubtypingInfo subtypingInfo = appViewWithLiveness.appInfo().computeSubtypingInfo();
@@ -585,8 +594,9 @@
runtimeTypeCheckInfo = null;
}
timing.end();
+ } else {
+ appView.setHorizontallyMergedClasses(HorizontallyMergedClasses.empty());
}
-
}
// None of the optimizations above should lead to the creation of type lattice elements.
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 4a68964..40be618 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -7,6 +7,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.errors.DexFileOverflowDiagnostic;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.features.FeatureSplitConfiguration;
@@ -42,6 +43,7 @@
import java.util.Collection;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
@@ -98,6 +100,7 @@
private boolean disableMinification = false;
private boolean disableVerticalClassMerging = false;
private boolean forceProguardCompatibility = false;
+ private Optional<Boolean> includeDataResources = Optional.empty();
private StringConsumer proguardMapConsumer = null;
private StringConsumer proguardUsageConsumer = null;
private StringConsumer proguardSeedsConsumer = null;
@@ -368,6 +371,7 @@
*/
@Override
public Builder setOutput(Path outputPath, OutputMode outputMode, boolean includeDataResources) {
+ this.includeDataResources = Optional.of(includeDataResources);
return super.setOutput(outputPath, outputMode, includeDataResources);
}
@@ -578,6 +582,7 @@
configuration.isObfuscating(),
disableVerticalClassMerging,
forceProguardCompatibility,
+ includeDataResources,
proguardMapConsumer,
proguardUsageConsumer,
proguardSeedsConsumer,
@@ -667,6 +672,7 @@
private final boolean enableMinification;
private final boolean disableVerticalClassMerging;
private final boolean forceProguardCompatibility;
+ private final Optional<Boolean> includeDataResources;
private final StringConsumer proguardMapConsumer;
private final StringConsumer proguardUsageConsumer;
private final StringConsumer proguardSeedsConsumer;
@@ -741,6 +747,7 @@
boolean enableMinification,
boolean disableVerticalClassMerging,
boolean forceProguardCompatibility,
+ Optional<Boolean> includeDataResources,
StringConsumer proguardMapConsumer,
StringConsumer proguardUsageConsumer,
StringConsumer proguardSeedsConsumer,
@@ -781,6 +788,7 @@
this.enableMinification = enableMinification;
this.disableVerticalClassMerging = disableVerticalClassMerging;
this.forceProguardCompatibility = forceProguardCompatibility;
+ this.includeDataResources = includeDataResources;
this.proguardMapConsumer = proguardMapConsumer;
this.proguardUsageConsumer = proguardUsageConsumer;
this.proguardSeedsConsumer = proguardSeedsConsumer;
@@ -803,6 +811,7 @@
enableMinification = false;
disableVerticalClassMerging = false;
forceProguardCompatibility = false;
+ includeDataResources = null;
proguardMapConsumer = null;
proguardUsageConsumer = null;
proguardSeedsConsumer = null;
@@ -974,6 +983,7 @@
internal.dumpInputToDirectory = null;
internal.dumpInputToFile = null;
}
+ internal.dumpOptions = dumpOptions();
return internal;
}
@@ -1002,4 +1012,18 @@
System.out.print(string);
}
}
+
+ private DumpOptions dumpOptions() {
+ DumpOptions.Builder builder = DumpOptions.builder(Tool.R8);
+ dumpBaseCommandOptions(builder);
+ return builder
+ .setIncludeDataResources(includeDataResources)
+ .setTreeShaking(getEnableTreeShaking())
+ .setMinification(getEnableMinification())
+ .setForceProguardCompatibility(forceProguardCompatibility)
+ .setFeatureSplitConfiguration(featureSplitConfiguration)
+ .setProguardConfiguration(proguardConfiguration)
+ .setDesugaredLibraryConfiguration(libraryConfiguration)
+ .build();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/bisect/BisectState.java b/src/main/java/com/android/tools/r8/bisect/BisectState.java
index cd3fe0a..85fa3eb 100644
--- a/src/main/java/com/android/tools/r8/bisect/BisectState.java
+++ b/src/main/java/com/android/tools/r8/bisect/BisectState.java
@@ -323,7 +323,7 @@
private static List<DexProgramClass> getSortedClasses(DexApplication app) {
List<DexProgramClass> classes = new ArrayList<>(app.classes());
- classes.sort((a, b) -> a.type.slowCompareTo(b.type, NamingLens.getIdentityLens()));
+ classes.sort((a, b) -> a.type.compareToWithNamingLens(b.type, NamingLens.getIdentityLens()));
return classes;
}
diff --git a/src/main/java/com/android/tools/r8/cf/CfVersion.java b/src/main/java/com/android/tools/r8/cf/CfVersion.java
index 84228b9..d90696c 100644
--- a/src/main/java/com/android/tools/r8/cf/CfVersion.java
+++ b/src/main/java/com/android/tools/r8/cf/CfVersion.java
@@ -3,10 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf;
-import java.util.Comparator;
+import com.android.tools.r8.utils.structural.DefaultCompareToVisitor;
+import com.android.tools.r8.utils.structural.Equatable;
+import com.android.tools.r8.utils.structural.HashCodeVisitor;
+import com.android.tools.r8.utils.structural.Ordered;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import org.objectweb.asm.Opcodes;
-public final class CfVersion implements Comparable<CfVersion> {
+public final class CfVersion implements Ordered<CfVersion> {
public static final CfVersion V1_1 = new CfVersion(Opcodes.V1_1);
public static final CfVersion V1_2 = new CfVersion(Opcodes.V1_2);
@@ -43,62 +47,23 @@
return version;
}
- public static CfVersion maxAllowNull(CfVersion v1, CfVersion v2) {
- assert v1 != null || v2 != null;
- if (v1 == null) {
- return v2;
- }
- if (v2 == null) {
- return v1;
- }
- return v1.max(v2);
- }
-
- public CfVersion max(CfVersion other) {
- return isLessThan(other) ? other : this;
- }
-
- public boolean isEqual(CfVersion other) {
- return version == other.version;
- }
-
- public boolean isLessThan(CfVersion other) {
- return compareTo(other) < 0;
- }
-
- public boolean isLessThanOrEqual(CfVersion other) {
- return compareTo(other) <= 0;
- }
-
- public boolean isGreaterThan(CfVersion other) {
- return compareTo(other) > 0;
- }
-
- public boolean isGreaterThanOrEqual(CfVersion other) {
- return compareTo(other) >= 0;
- }
-
- @Override
- public int compareTo(CfVersion o) {
- return Comparator.comparingInt(CfVersion::major)
- .thenComparingInt(CfVersion::minor)
- .compare(this, o);
+ private static void accept(StructuralSpecification<CfVersion, ?> spec) {
+ spec.withInt(CfVersion::major).withInt(CfVersion::minor);
}
@Override
public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (!(o instanceof CfVersion)) {
- return false;
- }
- return isEqual((CfVersion) o);
+ return Equatable.equalsImpl(this, o);
}
@Override
public int hashCode() {
- return version;
+ return HashCodeVisitor.run(this, CfVersion::accept);
+ }
+
+ @Override
+ public int compareTo(CfVersion other) {
+ return DefaultCompareToVisitor.run(this, other, CfVersion::accept);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
index 7af534e..a391dbb 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
@@ -44,7 +44,7 @@
@Override
public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return type.slowCompareTo(((CfCheckCast) other).type);
+ return type.compareTo(((CfCheckCast) other).type);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
index 9d4ffa0..ced7984 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
@@ -40,7 +40,7 @@
@Override
public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return type.slowCompareTo(((CfConstClass) other).type);
+ return type.compareTo(((CfConstClass) other).type);
}
public DexType getType() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
index 37d0ff4..9c3b478 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
@@ -44,7 +44,7 @@
@Override
public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return handle.slowCompareTo(((CfConstMethodHandle) other).handle);
+ return handle.compareTo(((CfConstMethodHandle) other).handle);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
index b6a560f..c285c80 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
@@ -44,7 +44,7 @@
@Override
public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return type.slowCompareTo(((CfConstMethodType) other).type);
+ return type.compareTo(((CfConstMethodType) other).type);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstString.java b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
index ee042dc..7ac89da 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfConstString.java
@@ -36,7 +36,7 @@
@Override
public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return string.slowCompareTo(other.asConstString().string);
+ return string.compareTo(other.asConstString().string);
}
public DexString getString() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
index 9b463fd..0915f0d 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -56,8 +56,8 @@
@Override
public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return Comparator.comparing(CfFieldInstruction::getField, DexField::slowCompareTo)
- .thenComparing(field -> field.declaringField, DexField::slowCompareTo)
+ return Comparator.comparing(CfFieldInstruction::getField)
+ .thenComparing(field -> field.declaringField)
.compare(this, (CfFieldInstruction) other);
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
index 2c81fce..53437ec 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
@@ -49,7 +49,7 @@
@Override
public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return clazz.slowCompareTo(((CfInitClass) other).clazz);
+ return clazz.compareTo(((CfInitClass) other).clazz);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
index 899ef31..1bbb32a 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
@@ -43,7 +43,7 @@
@Override
public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return type.slowCompareTo(other.asInstanceOf().type);
+ return type.compareTo(other.asInstanceOf().type);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index 4f5159b..3e07e17 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -62,7 +62,7 @@
public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
CfInvoke otherInvoke = other.asInvoke();
int itfDiff = Boolean.compare(itf, otherInvoke.itf);
- return itfDiff != 0 ? itfDiff : method.slowCompareTo(otherInvoke.method);
+ return itfDiff != 0 ? itfDiff : method.compareTo(otherInvoke.method);
}
public DexMethod getMethod() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
index d52e429..a504f8c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
@@ -52,7 +52,7 @@
@Override
public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
return Comparator.comparingInt(CfMultiANewArray::getDimensions)
- .thenComparing(CfMultiANewArray::getType, DexType::slowCompareTo)
+ .thenComparing(CfMultiANewArray::getType)
.compare(this, ((CfMultiANewArray) other));
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNew.java b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
index a4be181..41f468c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNew.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
@@ -44,7 +44,7 @@
@Override
public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return type.slowCompareTo(((CfNew) other).type);
+ return type.compareTo(((CfNew) other).type);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
index 794227e..414bd58 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
@@ -47,7 +47,7 @@
@Override
public int internalCompareTo(CfInstruction other, CfCompareHelper helper) {
- return type.slowCompareTo(((CfNewArray) other).type);
+ return type.compareTo(((CfNewArray) other).type);
}
private int getPrimitiveTypeCode() {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java b/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java
index 01ff28f..a93f320 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java
@@ -52,7 +52,7 @@
public int compareTo(CfTryCatch other, CfCompareHelper helper) {
return Comparator.comparing((CfTryCatch c) -> c.start, helper::compareLabels)
.thenComparing(c -> c.end, helper::compareLabels)
- .thenComparing(c -> c.guards, ComparatorUtils.listComparator(DexType::slowCompareTo))
+ .thenComparing(c -> c.guards, ComparatorUtils.listComparator())
.thenComparing(c -> c.targets, ComparatorUtils.listComparator(helper::compareLabels))
.compare(this, other);
}
diff --git a/src/main/java/com/android/tools/r8/code/CheckCast.java b/src/main/java/com/android/tools/r8/code/CheckCast.java
index 06edf90..32df17b 100644
--- a/src/main/java/com/android/tools/r8/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/code/CheckCast.java
@@ -45,7 +45,7 @@
@Override
int internalCompareBBBB(Format21c<?> other) {
- return BBBB.slowCompareTo((DexType) other.BBBB);
+ return BBBB.compareTo((DexType) other.BBBB);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/ConstClass.java b/src/main/java/com/android/tools/r8/code/ConstClass.java
index 5562a69..ce1c26b 100644
--- a/src/main/java/com/android/tools/r8/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/code/ConstClass.java
@@ -30,7 +30,7 @@
@Override
int internalCompareBBBB(Format21c<?> other) {
- return BBBB.slowCompareTo((DexType) other.BBBB);
+ return BBBB.compareTo((DexType) other.BBBB);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
index d69329a..0409f06 100644
--- a/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
@@ -52,7 +52,7 @@
@Override
int internalCompareBBBB(Format21c<?> other) {
- return BBBB.slowCompareTo((DexMethodHandle) other.BBBB);
+ return BBBB.compareTo((DexMethodHandle) other.BBBB);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/code/ConstMethodType.java
index ba6e89f..8f54c24 100644
--- a/src/main/java/com/android/tools/r8/code/ConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/code/ConstMethodType.java
@@ -51,7 +51,7 @@
@Override
int internalCompareBBBB(Format21c<?> other) {
- return BBBB.slowCompareTo((DexProto) other.BBBB);
+ return BBBB.compareTo((DexProto) other.BBBB);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/ConstString.java b/src/main/java/com/android/tools/r8/code/ConstString.java
index 0b44f8a..e9e910d 100644
--- a/src/main/java/com/android/tools/r8/code/ConstString.java
+++ b/src/main/java/com/android/tools/r8/code/ConstString.java
@@ -35,7 +35,7 @@
@Override
int internalCompareBBBB(Format21c<?> other) {
- return BBBB.slowCompareTo((DexString) other.BBBB);
+ return BBBB.compareTo((DexString) other.BBBB);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/DexInitClass.java b/src/main/java/com/android/tools/r8/code/DexInitClass.java
index 2d9cda9..ab39c88 100644
--- a/src/main/java/com/android/tools/r8/code/DexInitClass.java
+++ b/src/main/java/com/android/tools/r8/code/DexInitClass.java
@@ -129,7 +129,7 @@
@Override
final int internalCompareTo(Instruction other) {
return Comparator.comparingInt((DexInitClass i) -> i.dest)
- .thenComparing(i -> i.clazz, DexType::slowCompareTo)
+ .thenComparing(i -> i.clazz)
.compare(this, (DexInitClass) other);
}
diff --git a/src/main/java/com/android/tools/r8/code/FilledNewArray.java b/src/main/java/com/android/tools/r8/code/FilledNewArray.java
index b432eec..a1ee0ba 100644
--- a/src/main/java/com/android/tools/r8/code/FilledNewArray.java
+++ b/src/main/java/com/android/tools/r8/code/FilledNewArray.java
@@ -44,7 +44,7 @@
@Override
int internalCompareBBBB(Format35c<?> other) {
- return BBBB.slowCompareTo((DexType) other.BBBB);
+ return BBBB.compareTo((DexType) other.BBBB);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java b/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
index 13dc9a5..03594c2 100644
--- a/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
+++ b/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
@@ -44,7 +44,7 @@
@Override
int internalCompareBBBB(Format3rc<?> other) {
- return BBBB.slowCompareTo((DexType) other.BBBB);
+ return BBBB.compareTo((DexType) other.BBBB);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/Format31c.java b/src/main/java/com/android/tools/r8/code/Format31c.java
index a1ae51e..672057b 100644
--- a/src/main/java/com/android/tools/r8/code/Format31c.java
+++ b/src/main/java/com/android/tools/r8/code/Format31c.java
@@ -54,7 +54,7 @@
final int internalCompareTo(Instruction other) {
Format31c o = (Format31c) other;
int diff = Short.compare(AA, o.AA);
- return diff != 0 ? diff : BBBBBBBB.slowCompareTo(o.BBBBBBBB);
+ return diff != 0 ? diff : BBBBBBBB.compareTo(o.BBBBBBBB);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/Format45cc.java b/src/main/java/com/android/tools/r8/code/Format45cc.java
index aa62780..bb0e1c7 100644
--- a/src/main/java/com/android/tools/r8/code/Format45cc.java
+++ b/src/main/java/com/android/tools/r8/code/Format45cc.java
@@ -90,8 +90,8 @@
if (diff != 0) {
return diff;
}
- int bDiff = BBBB.slowCompareTo(o.BBBB);
- return bDiff != 0 ? bDiff : HHHH.slowCompareTo(o.HHHH);
+ int bDiff = BBBB.compareTo(o.BBBB);
+ return bDiff != 0 ? bDiff : HHHH.compareTo(o.HHHH);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/Format4rcc.java b/src/main/java/com/android/tools/r8/code/Format4rcc.java
index 690b230..f29357d 100644
--- a/src/main/java/com/android/tools/r8/code/Format4rcc.java
+++ b/src/main/java/com/android/tools/r8/code/Format4rcc.java
@@ -73,8 +73,8 @@
final int internalCompareTo(Instruction other) {
return Comparator.comparingInt((Format4rcc i) -> i.AA)
.thenComparingInt(i -> i.CCCC)
- .thenComparing(i -> i.BBBB, DexMethod::slowCompareTo)
- .thenComparing(i -> i.HHHH, DexProto::slowCompareTo)
+ .thenComparing(i -> i.BBBB)
+ .thenComparing(i -> i.HHHH)
.compare(this, (Format4rcc) other);
}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/code/InvokeMethod.java
index de46296..ec8e6c4 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeMethod.java
@@ -43,7 +43,7 @@
@Override
int internalCompareBBBB(Format35c<?> other) {
- return BBBB.slowCompareTo((DexMethod) other.BBBB);
+ return BBBB.compareTo((DexMethod) other.BBBB);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/InvokeMethodRange.java b/src/main/java/com/android/tools/r8/code/InvokeMethodRange.java
index 102d793..19c2bf1 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeMethodRange.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeMethodRange.java
@@ -43,7 +43,7 @@
@Override
int internalCompareBBBB(Format3rc<?> other) {
- return BBBB.slowCompareTo((DexMethod) other.BBBB);
+ return BBBB.compareTo((DexMethod) other.BBBB);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/NewInstance.java b/src/main/java/com/android/tools/r8/code/NewInstance.java
index d3d25c6..0b90d1a 100644
--- a/src/main/java/com/android/tools/r8/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/code/NewInstance.java
@@ -45,7 +45,7 @@
@Override
int internalCompareBBBB(Format21c<?> other) {
- return BBBB.slowCompareTo((DexType) other.BBBB);
+ return BBBB.compareTo((DexType) other.BBBB);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/code/SgetOrSput.java b/src/main/java/com/android/tools/r8/code/SgetOrSput.java
index 48112fc..4524f8e 100644
--- a/src/main/java/com/android/tools/r8/code/SgetOrSput.java
+++ b/src/main/java/com/android/tools/r8/code/SgetOrSput.java
@@ -50,6 +50,6 @@
@Override
int internalCompareBBBB(Format21c<?> other) {
- return BBBB.slowCompareTo((DexField) other.BBBB);
+ return BBBB.compareTo((DexField) other.BBBB);
}
}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index 2f3c56e..b46cd9b 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -184,7 +184,7 @@
}
if (dumpOutput != null) {
timing.begin("ApplicationReader.dump");
- dumpInputToFile(inputApp, dumpOutput, options);
+ inputApp.dump(dumpOutput, options.dumpOptions, options.reporter, options.dexItemFactory());
if (cleanDump) {
Files.delete(dumpOutput);
}
@@ -230,10 +230,6 @@
}
}
- private static void dumpInputToFile(AndroidApp app, Path output, InternalOptions options) {
- app.dump(output, options);
- }
-
private static boolean verifyMainDexOptionsCompatible(
AndroidApp inputApp, InternalOptions options) {
if (!options.isGeneratingDex()) {
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 77a7d70..2ad4409 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -301,8 +301,7 @@
}
}
ObjectToOffsetMapping objectMapping =
- virtualFile.computeMapping(
- appView.appInfo(), graphLens, namingLens, initClassLens);
+ virtualFile.computeMapping(appView, graphLens, namingLens, initClassLens);
MethodToCodeObjectMapping codeMapping =
rewriteCodeWithJumboStrings(
objectMapping, virtualFile.classes(), appView.appInfo().app());
@@ -614,8 +613,8 @@
return MethodToCodeObjectMapping.fromMethodBacking();
}
// If the globally highest sorting string is not a jumbo string this is also a no-op.
- if (application.highestSortingString != null &&
- application.highestSortingString.slowCompareTo(mapping.getFirstJumboString()) < 0) {
+ if (application.highestSortingString != null
+ && application.highestSortingString.compareTo(mapping.getFirstJumboString()) < 0) {
return MethodToCodeObjectMapping.fromMethodBacking();
}
}
@@ -672,7 +671,7 @@
StringBuilder builder = new StringBuilder();
List<DexType> list = new ArrayList<>(mainDexClasses.size());
mainDexClasses.forEach(list::add);
- list.sort(DexType::slowCompareTo);
+ list.sort(DexType::compareTo);
list.forEach(
type -> builder.append(mapMainDexListName(type, namingLens)).append('\n'));
return builder.toString();
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index 24d3a36..48fded1 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -609,7 +609,7 @@
// compareTo instead of slowCompareTo. That would require us to assign indices during
// reading. Those indices should be cleared after reading to make sure that we resort
// everything correctly at the end.
- while (index < annotations.length && annotations[index].item.slowCompareTo(item) < 0) {
+ while (index < annotations.length && annotations[index].item.compareTo(item) < 0) {
index++;
}
if (index >= annotations.length || !annotations[index].item.equals(item)) {
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 74201ff..c07e0f5 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -133,7 +133,7 @@
Log.verbose(FileWriter.class, "Writing encoded annotation @ %08x", dest.position());
}
List<DexAnnotationElement> elements = new ArrayList<>(Arrays.asList(annotation.elements));
- elements.sort((a, b) -> a.name.slowCompareTo(b.name, mapping.getNamingLens()));
+ elements.sort((a, b) -> a.name.compareToWithNamingLens(b.name, mapping.getNamingLens()));
dest.putUleb128(mapping.getOffsetFor(annotation.type));
dest.putUleb128(elements.size());
for (DexAnnotationElement element : elements) {
@@ -579,7 +579,8 @@
Log.verbose(getClass(), "Writing AnnotationSet @ 0x%08x.", dest.position());
}
List<DexAnnotation> annotations = new ArrayList<>(Arrays.asList(set.annotations));
- annotations.sort((a, b) -> a.annotation.type.slowCompareTo(b.annotation.type, namingLens));
+ annotations.sort(
+ (a, b) -> a.annotation.type.compareToWithNamingLens(b.annotation.type, namingLens));
dest.putInt(annotations.size());
for (DexAnnotation annotation : annotations) {
dest.putInt(mixedSectionOffsets.getOffsetFor(annotation));
@@ -629,7 +630,7 @@
private void writeEncodedFields(List<DexEncodedField> unsortedFields) {
List<DexEncodedField> fields = new ArrayList<>(unsortedFields);
- fields.sort((a, b) -> a.field.slowCompareTo(b.field, namingLens));
+ fields.sort((a, b) -> a.field.compareToWithNamingLens(b.field, namingLens));
int currentOffset = 0;
for (DexEncodedField field : fields) {
assert field.validateDexValue(application.dexItemFactory);
@@ -645,7 +646,7 @@
private void writeEncodedMethods(
Iterable<DexEncodedMethod> unsortedMethods, boolean isSharedSynthetic) {
List<DexEncodedMethod> methods = IterableUtils.toNewArrayList(unsortedMethods);
- methods.sort((a, b) -> a.method.slowCompareTo(b.method, namingLens));
+ methods.sort((a, b) -> a.method.compareToWithNamingLens(b.method, namingLens));
int currentOffset = 0;
for (DexEncodedMethod method : methods) {
int nextOffset = mapping.getOffsetFor(method.method);
diff --git a/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java b/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
index cad079b..8e8750a 100644
--- a/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
+++ b/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
@@ -45,7 +45,7 @@
public class InheritanceClassInDexDistributor {
private static final Comparator<DexProgramClass> DEX_PROGRAM_CLASS_COMPARATOR =
- (a, b) -> a.type.descriptor.slowCompareTo(b.type.descriptor);
+ (a, b) -> a.type.descriptor.compareTo(b.type.descriptor);
private static final int DEX_FULL_ENOUGH_THRESHOLD = VirtualFile.MAX_ENTRIES - 100;
private final ExecutorService executorService;
diff --git a/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java b/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
index d1f8749..e402ce5 100644
--- a/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
+++ b/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
@@ -281,7 +281,7 @@
instruction.setOffset(orignalOffset + offsetDelta);
if (instruction instanceof ConstString) {
ConstString string = (ConstString) instruction;
- if (string.getString().slowCompareTo(firstJumboString) >= 0) {
+ if (string.getString().compareTo(firstJumboString) >= 0) {
ConstStringJumbo jumboString = new ConstStringJumbo(string.AA, string.getString());
jumboString.setOffset(string.getOffset());
offsetDelta++;
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index b808097..108df1d 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
-import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexField;
@@ -210,10 +209,10 @@
}
public ObjectToOffsetMapping computeMapping(
- AppInfo appInfo, GraphLens graphLens, NamingLens namingLens, InitClassLens initClassLens) {
+ AppView<?> appView, GraphLens graphLens, NamingLens namingLens, InitClassLens initClassLens) {
assert transaction.isEmpty();
return new ObjectToOffsetMapping(
- appInfo,
+ appView,
graphLens,
namingLens,
initClassLens,
@@ -742,7 +741,7 @@
this.graphLens = graphLens;
this.initClassLens = initClassLens;
this.namingLens = namingLens;
- this.rewriter = new LensCodeRewriterUtils(appView, graphLens);
+ this.rewriter = new LensCodeRewriterUtils(appView);
}
private <T extends DexItem> boolean maybeInsert(T item, Set<T> set, Set<T> baseSet) {
diff --git a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
index 519bd27..b6588df 100644
--- a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
+++ b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
import com.google.common.collect.Sets;
import java.util.IdentityHashMap;
import java.util.Map;
@@ -39,8 +40,14 @@
public static ClassToFeatureSplitMap createInitialClassToFeatureSplitMap(
InternalOptions options) {
- DexItemFactory dexItemFactory = options.dexItemFactory();
- FeatureSplitConfiguration featureSplitConfiguration = options.featureSplitConfiguration;
+ return createInitialClassToFeatureSplitMap(
+ options.dexItemFactory(), options.featureSplitConfiguration, options.reporter);
+ }
+
+ public static ClassToFeatureSplitMap createInitialClassToFeatureSplitMap(
+ DexItemFactory dexItemFactory,
+ FeatureSplitConfiguration featureSplitConfiguration,
+ Reporter reporter) {
ClassToFeatureSplitMap result = new ClassToFeatureSplitMap();
if (featureSplitConfiguration == null) {
@@ -58,7 +65,7 @@
}
}
} catch (ResourceException e) {
- throw options.reporter.fatalError(e.getMessage());
+ throw reporter.fatalError(e.getMessage());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/AbstractAccessContexts.java b/src/main/java/com/android/tools/r8/graph/AbstractAccessContexts.java
index 4615f1f..8767555 100644
--- a/src/main/java/com/android/tools/r8/graph/AbstractAccessContexts.java
+++ b/src/main/java/com/android/tools/r8/graph/AbstractAccessContexts.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import java.util.IdentityHashMap;
import java.util.Map;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -86,6 +87,8 @@
return UnknownAccessContexts.getInstance();
}
+ public abstract AbstractAccessContexts join(AbstractAccessContexts contexts);
+
public static class EmptyAccessContexts extends AbstractAccessContexts {
public static EmptyAccessContexts INSTANCE = new EmptyAccessContexts();
@@ -140,6 +143,11 @@
AbstractAccessContexts rewrittenWithLens(DexDefinitionSupplier definitions, GraphLens lens) {
return this;
}
+
+ @Override
+ public AbstractAccessContexts join(AbstractAccessContexts contexts) {
+ return contexts;
+ }
}
public static class ConcreteAccessContexts extends AbstractAccessContexts {
@@ -305,6 +313,28 @@
});
return new ConcreteAccessContexts(newAccessesWithContexts);
}
+
+ @Override
+ public AbstractAccessContexts join(AbstractAccessContexts contexts) {
+ if (contexts.isEmpty()) {
+ return this;
+ }
+ if (contexts.isTop()) {
+ return contexts;
+ }
+ Map<DexField, ProgramMethodSet> newAccessesWithContexts = new IdentityHashMap<>();
+ accessesWithContexts.forEach(
+ (field, methodSet) ->
+ newAccessesWithContexts.put(field, ProgramMethodSet.create(methodSet)));
+
+ BiConsumer<DexField, ProgramMethodSet> addAllMethods =
+ (field, methodSet) ->
+ newAccessesWithContexts
+ .computeIfAbsent(field, ignore -> ProgramMethodSet.create())
+ .addAll(methodSet);
+ contexts.asConcrete().accessesWithContexts.forEach(addAllMethods);
+ return new ConcreteAccessContexts(newAccessesWithContexts);
+ }
}
public static class UnknownAccessContexts extends AbstractAccessContexts {
@@ -362,5 +392,10 @@
AbstractAccessContexts rewrittenWithLens(DexDefinitionSupplier definitions, GraphLens lens) {
return this;
}
+
+ @Override
+ public AbstractAccessContexts join(AbstractAccessContexts contexts) {
+ return this;
+ }
}
}
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 ef9b742..70db016 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
import com.android.tools.r8.graph.classmerging.MergedClasses;
import com.android.tools.r8.graph.classmerging.MergedClassesCollection;
+import com.android.tools.r8.graph.classmerging.StaticallyMergedClasses;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.ir.analysis.proto.EnumLiteProtoShrinker;
@@ -85,6 +86,7 @@
private InitializedClassesInInstanceMethods initializedClassesInInstanceMethods;
private HorizontallyMergedLambdaClasses horizontallyMergedLambdaClasses;
private HorizontallyMergedClasses horizontallyMergedClasses;
+ private StaticallyMergedClasses staticallyMergedClasses;
private VerticallyMergedClasses verticallyMergedClasses;
private EnumValueInfoMapCollection unboxedEnums = EnumValueInfoMapCollection.empty();
// TODO(b/169115389): Remove
@@ -437,6 +439,9 @@
public MergedClassesCollection allMergedClasses() {
MergedClassesCollection collection = new MergedClassesCollection();
+ if (horizontallyMergedClasses != null) {
+ collection.add(horizontallyMergedClasses);
+ }
if (horizontallyMergedLambdaClasses != null) {
collection.add(horizontallyMergedLambdaClasses);
}
@@ -470,8 +475,8 @@
}
/**
- * Get the result of horizontal class merging. Returns null if horizontal lambda class merging has
- * not been run.
+ * Get the result of horizontal class merging. Returns null if horizontal class merging has not
+ * been run.
*/
public HorizontallyMergedClasses horizontallyMergedClasses() {
return horizontallyMergedClasses;
@@ -484,6 +489,19 @@
}
/**
+ * Get the result of static class merging. Returns null if static class merging has not been run.
+ */
+ public StaticallyMergedClasses staticallyMergedClasses() {
+ return staticallyMergedClasses;
+ }
+
+ public void setStaticallyMergedClasses(StaticallyMergedClasses staticallyMergedClasses) {
+ assert this.staticallyMergedClasses == null;
+ this.staticallyMergedClasses = staticallyMergedClasses;
+ testing().staticallyMergedClassesConsumer.accept(dexItemFactory(), staticallyMergedClasses);
+ }
+
+ /**
* Get the result of vertical class merging. Returns null if vertical class merging has not been
* run.
*/
diff --git a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
index bae65e8..887f10d 100644
--- a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
@@ -102,7 +102,7 @@
@Override
public Iterable<DexType> getOriginalTypes(DexType type) {
- Set<DexType> originalTypes = renamedTypeNames.getKeys(type);
+ Set<DexType> originalTypes = renamedTypeNames.getKeysOrNull(type);
if (originalTypes == null) {
return ImmutableList.of(type);
}
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index d6ae59f..c17ff09 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -312,7 +312,7 @@
for (CfInstruction instruction : instructions) {
if (instruction instanceof CfFrame
&& (classFileVersion.isLessThan(CfVersion.V1_6)
- || (classFileVersion.isEqual(CfVersion.V1_6)
+ || (classFileVersion.isEqualTo(CfVersion.V1_6)
&& !options.shouldKeepStackMapTable()))) {
continue;
}
diff --git a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
index b43a42c..7998171 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
@@ -109,7 +109,7 @@
public boolean areValid(CfVersion version, boolean isPackageInfo) {
if (isInterface()) {
// We ignore the super flags prior to JDK 9, as so did the VM.
- if (version.isGreaterThanOrEqual(CfVersion.V9) && isSuper()) {
+ if (version.isGreaterThanOrEqualTo(CfVersion.V9) && isSuper()) {
return false;
}
// When not coming from DEX input we require interfaces to be abstract - except for
diff --git a/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java b/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
index 3bb9931..333b8a7 100644
--- a/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
@@ -33,9 +33,9 @@
@Override
public int compareTo(DebugLocalInfo other) {
- return Comparator.comparing((DebugLocalInfo info) -> info.name, DexString::slowCompareTo)
- .thenComparing(info -> info.type, DexType::slowCompareTo)
- .thenComparing(info -> info.signature, Comparator.nullsFirst(DexString::slowCompareTo))
+ return Comparator.comparing((DebugLocalInfo info) -> info.name)
+ .thenComparing(info -> info.type)
+ .thenComparing(info -> info.signature, Comparator.nullsFirst(DexString::compareTo))
.compare(this, other);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
index 04f59d7..2ee613c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
@@ -46,17 +46,17 @@
}
public List<DexEncodedMethod> sortMethodAnnotations(NamingLens namingLens) {
- methodAnnotations.sort((a, b) -> a.method.slowCompareTo(b.method, namingLens));
+ methodAnnotations.sort((a, b) -> a.method.compareToWithNamingLens(b.method, namingLens));
return methodAnnotations;
}
public List<DexEncodedMethod> sortParameterAnnotations(NamingLens namingLens) {
- parameterAnnotations.sort((a, b) -> a.method.slowCompareTo(b.method, namingLens));
+ parameterAnnotations.sort((a, b) -> a.method.compareToWithNamingLens(b.method, namingLens));
return parameterAnnotations;
}
public List<DexEncodedField> sortFieldAnnotations(NamingLens namingLens) {
- fieldAnnotations.sort((a, b) -> a.field.slowCompareTo(b.field, namingLens));
+ fieldAnnotations.sort((a, b) -> a.field.compareToWithNamingLens(b.field, namingLens));
return fieldAnnotations;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
index e13faf8..ba71338 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import static com.android.tools.r8.utils.PredicateUtils.not;
+
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.naming.NamingLens;
@@ -99,7 +101,8 @@
return;
}
Arrays.sort(
- annotations, (a, b) -> a.annotation.type.slowCompareTo(b.annotation.type, namingLens));
+ annotations,
+ (a, b) -> a.annotation.type.compareToWithNamingLens(b.annotation.type, namingLens));
for (DexAnnotation annotation : annotations) {
annotation.annotation.sort();
}
@@ -157,7 +160,11 @@
}
public DexAnnotationSet keepIf(Predicate<DexAnnotation> filter) {
- return rewrite(anno -> filter.test(anno) ? anno : null);
+ return removeIf(not(filter));
+ }
+
+ public DexAnnotationSet removeIf(Predicate<DexAnnotation> filter) {
+ return rewrite(annotation -> filter.test(annotation) ? null : annotation);
}
public DexAnnotationSet rewrite(Function<DexAnnotation, DexAnnotation> rewriter) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index abb91ed..6e38ed4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -109,7 +109,7 @@
// that.
if (options.testing.deterministicSortingBasedOnDexType) {
// To keep the order deterministic, we sort the classes by their type, which is a unique key.
- classes.sort((a, b) -> a.type.slowCompareTo(b.type));
+ classes.sort((a, b) -> a.type.compareTo(b.type));
}
return classes;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCallSite.java b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
index bcb8873..b38e016 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCallSite.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
@@ -158,7 +158,7 @@
@Override
public int compareTo(DexCallSite other) {
assert method != null && other.method != null;
- int methodCompare = method.slowCompareTo(other.method);
+ int methodCompare = method.compareTo(other.method);
if (methodCompare != 0) {
return methodCompare;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 45130dc..5215ada 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -3,13 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import static com.google.common.base.Predicates.alwaysFalse;
+
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.OptionalBool;
@@ -673,57 +674,28 @@
}
public boolean classInitializationMayHaveSideEffects(AppView<?> appView) {
- return classInitializationMayHaveSideEffects(
- appView, Predicates.alwaysFalse(), Sets.newIdentityHashSet());
- }
-
- public boolean classInitializationMayHaveSideEffectsInContext(
- AppView<AppInfoWithLiveness> appView, ProgramDefinition context) {
- return classInitializationMayHaveSideEffects(
- appView,
- // Types that are a super type of the current context are guaranteed to be initialized
- // already.
- type -> appView.appInfo().isSubtype(context.getContextType(), type),
- Sets.newIdentityHashSet());
+ return classInitializationMayHaveSideEffects(appView, alwaysFalse());
}
public boolean classInitializationMayHaveSideEffects(
- AppView<?> appView, Predicate<DexType> ignore, Set<DexType> seen) {
- if (!seen.add(type)) {
- return false;
- }
- if (ignore.test(type)) {
- return false;
- }
- if (isLibraryClass()) {
- if (isInterface()) {
- return appView.options().libraryInterfacesMayHaveStaticInitialization;
- }
- if (appView.dexItemFactory().libraryClassesWithoutStaticInitialization.contains(type)) {
- return false;
- }
- }
- if (hasClassInitializerThatCannotBePostponed()) {
- return true;
- }
- if (defaultValuesForStaticFieldsMayTriggerAllocation()) {
- return true;
- }
- return initializationOfParentTypesMayHaveSideEffects(appView, ignore, seen);
+ AppView<?> appView, Predicate<DexType> ignore) {
+ return internalClassOrInterfaceMayHaveInitializationSideEffects(
+ appView, this, ignore, Sets.newIdentityHashSet());
}
- private boolean hasClassInitializerThatCannotBePostponed() {
- if (isLibraryClass()) {
- // We don't know for library classes in general but assume that java.lang.Object is safe.
- return superType != null;
- }
- DexEncodedMethod clinit = getClassInitializer();
- if (clinit == null || clinit.getCode() == null) {
- return false;
- }
- return !clinit.getOptimizationInfo().classInitializerMayBePostponed();
+ public final boolean classInitializationMayHaveSideEffectsInContext(
+ AppView<?> appView, ProgramDefinition context) {
+ // Types that are a super type of the current context are guaranteed to be initialized already.
+ return classInitializationMayHaveSideEffects(
+ appView, type -> appView.isSubtype(context.getContextType(), type).isTrue());
}
+ abstract boolean internalClassOrInterfaceMayHaveInitializationSideEffects(
+ AppView<?> appView,
+ DexClass initialAccessHolder,
+ Predicate<DexType> ignore,
+ Set<DexType> seen);
+
public void forEachImmediateSupertype(Consumer<DexType> fn) {
if (superType != null) {
fn.accept(superType);
@@ -742,32 +714,14 @@
return () -> iterator;
}
- public boolean initializationOfParentTypesMayHaveSideEffects(AppView<?> appView) {
- return initializationOfParentTypesMayHaveSideEffects(
- appView, Predicates.alwaysFalse(), Sets.newIdentityHashSet());
- }
-
- public boolean initializationOfParentTypesMayHaveSideEffects(
- AppView<?> appView, Predicate<DexType> ignore, Set<DexType> seen) {
- for (DexType iface : interfaces.values) {
- if (iface.classInitializationMayHaveSideEffects(appView, ignore, seen)) {
- return true;
- }
- }
- if (superType != null
- && superType.classInitializationMayHaveSideEffects(appView, ignore, seen)) {
- return true;
- }
- return false;
- }
-
public boolean definesFinalizer(DexItemFactory factory) {
return lookupVirtualMethod(factory.objectMembers.finalize) != null;
}
public boolean defaultValuesForStaticFieldsMayTriggerAllocation() {
return staticFields().stream()
- .anyMatch(field -> field.getStaticValue().mayHaveSideEffects());
+ .anyMatch(
+ field -> field.hasExplicitStaticValue() && field.getStaticValue().mayHaveSideEffects());
}
public List<InnerClassAttribute> getInnerClasses() {
@@ -904,6 +858,10 @@
/** Returns kotlin class info if the class is synthesized by kotlin compiler. */
public abstract KotlinClassLevelInfo getKotlinInfo();
+ public boolean hasStaticFields() {
+ return staticFields.length > 0;
+ }
+
public boolean hasInstanceFields() {
return instanceFields.length > 0;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
index 410f985..c97a1a4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
@@ -11,6 +11,8 @@
import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
import com.android.tools.r8.origin.Origin;
import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
import java.util.function.Supplier;
public class DexClasspathClass extends DexClass implements Supplier<DexClasspathClass> {
@@ -90,4 +92,16 @@
public DexClasspathClass get() {
return this;
}
+
+ @Override
+ boolean internalClassOrInterfaceMayHaveInitializationSideEffects(
+ AppView<?> appView,
+ DexClass initialAccessHolder,
+ Predicate<DexType> ignore,
+ Set<DexType> seen) {
+ if (!seen.add(getType()) || ignore.test(getType())) {
+ return false;
+ }
+ return !isInterface() || appView.options().classpathInterfacesMayHaveStaticInitialization;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 7a9c724..ab974b3 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -430,7 +430,7 @@
private void updateHighestSortingString(DexString candidate) {
assert candidate != null;
- if (highestSortingString == null || highestSortingString.slowCompareTo(candidate) < 0) {
+ if (highestSortingString == null || highestSortingString.compareTo(candidate) < 0) {
highestSortingString = candidate;
}
}
@@ -621,7 +621,7 @@
return 0;
}
return Comparator.comparingInt((TypeAddrPair p) -> p.addr)
- .thenComparing(p -> p.type, DexType::slowCompareTo)
+ .thenComparing(p -> p.type)
.compare(this, other);
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
index 903686d..16842b0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
@@ -300,9 +300,9 @@
@Override
int internalCompareTo(DexDebugEvent other) {
return Comparator.comparingInt((StartLocal e) -> e.registerNum)
- .thenComparing(e -> e.name, DexString::slowCompareTo)
- .thenComparing(e -> e.type, DexType::slowCompareTo)
- .thenComparing(e -> e.signature, Comparator.nullsFirst(DexString::slowCompareTo))
+ .thenComparing(e -> e.name)
+ .thenComparing(e -> e.type)
+ .thenComparing(e -> e.signature, Comparator.nullsFirst(DexString::compareTo))
.compare(this, (StartLocal) other);
}
}
@@ -431,7 +431,7 @@
@Override
int internalCompareTo(DexDebugEvent other) {
- return fileName.slowCompareTo(((SetFile) other).fileName);
+ return fileName.compareTo(((SetFile) other).fileName);
}
}
@@ -473,7 +473,7 @@
@Override
int internalCompareTo(DexDebugEvent other) {
- return Comparator.comparing((SetInlineFrame e) -> e.callee, DexMethod::slowCompareTo)
+ return Comparator.comparing((SetInlineFrame e) -> e.callee, DexMethod::compareTo)
.thenComparing(e -> e.caller, Comparator.nullsFirst(Position::compareTo))
.compare(this, (SetInlineFrame) other);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
index 5e44a57..5bfbd8d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
@@ -54,7 +54,7 @@
return Comparator.comparingInt((DexDebugInfo i) -> i.startLine)
.thenComparing(
i -> i.parameters,
- ComparatorUtils.arrayComparator(Comparator.nullsFirst(DexString::slowCompareTo)))
+ ComparatorUtils.arrayComparator(Comparator.nullsFirst(DexString::compareTo)))
.thenComparing(i -> i.events, ComparatorUtils.arrayComparator())
.compare(this, other);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDefinition.java b/src/main/java/com/android/tools/r8/graph/DexDefinition.java
index 7259345..0153cd2 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDefinition.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDefinition.java
@@ -40,6 +40,10 @@
this.annotations = annotations;
}
+ public void removeAnnotations(Predicate<DexAnnotation> predicate) {
+ setAnnotations(annotations().removeIf(predicate));
+ }
+
public boolean isDexClass() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
index cb4eb4e..051dd92 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
@@ -78,7 +78,7 @@
assert sorted == sortedHashCode();
return;
}
- Arrays.sort(elements, (a, b) -> a.name.slowCompareTo(b.name));
+ Arrays.sort(elements, (a, b) -> a.name.compareTo(b.name));
for (DexAnnotationElement element : elements) {
element.value.sort();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 159c27b..16879f0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -20,7 +20,6 @@
import com.android.tools.r8.ir.optimize.info.MutableFieldOptimizationInfo;
import com.android.tools.r8.kotlin.KotlinFieldLevelInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.google.common.collect.Sets;
public class DexEncodedField extends DexEncodedMember<DexEncodedField, DexField> {
public static final DexEncodedField[] EMPTY_ARRAY = {};
@@ -267,32 +266,6 @@
return null;
}
- public boolean mayTriggerClassInitializationSideEffects(
- AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
- // Only static field matters when it comes to class initialization side effects.
- if (!isStatic()) {
- return false;
- }
- DexClass clazz = appView.definitionFor(field.holder);
- if (clazz == null) {
- return true;
- }
- if (clazz.classInitializationMayHaveSideEffects(
- appView,
- // Types that are a super type of the current context are guaranteed to be initialized
- // already.
- type -> appView.appInfo().isSubtype(context.getHolderType(), type),
- Sets.newIdentityHashSet())) {
- // Ignore class initialization side-effects for dead proto extension fields to ensure that
- // we force replace these field reads by null.
- boolean ignore =
- appView.withGeneratedExtensionRegistryShrinker(
- shrinker -> shrinker.isDeadProtoExtensionField(field), false);
- return !ignore;
- }
- return false;
- }
-
public DexEncodedField toTypeSubstitutedField(DexField field) {
if (this.field == field) {
return this;
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index c353e8a..e84e3e8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -75,6 +75,11 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.structural.CompareToVisitorWithTypeEquivalence;
+import com.android.tools.r8.utils.structural.HashingVisitorWithTypeEquivalence;
+import com.android.tools.r8.utils.structural.Ordered;
+import com.android.tools.r8.utils.structural.RepresentativeMap;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import com.google.common.collect.ImmutableList;
import com.google.common.hash.Hasher;
import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
@@ -82,7 +87,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
@@ -318,14 +322,50 @@
return deprecated;
}
- public void hashSyntheticContent(Hasher hasher) {
- // Method holder does not contribute to the synthetic hash (it is freely chosen).
- // Method name does not contribute to the synthetic hash (it is freely chosen).
- method.proto.hashSyntheticContent(hasher);
- hasher.putInt(accessFlags.getAsCfAccessFlags());
- assert annotations().isEmpty();
- assert parameterAnnotationsList.isEmpty();
- assert code != null;
+ // Visitor specifying the structure of the method with respect to its "synthetic" content.
+ // TODO(b/171867022): Generalize this so that it determines any method in full.
+ private static void syntheticSpecify(StructuralSpecification<DexEncodedMethod, ?> spec) {
+ spec.withAssert(m1 -> m1.annotations().isEmpty())
+ .withAssert(m1 -> m1.parameterAnnotationsList.isEmpty())
+ .withAssert(m1 -> m1.code != null)
+ .withItem(DexEncodedMethod::getHolderType)
+ .withItem(DexEncodedMethod::getName)
+ .withInt(m -> m.getAccessFlags().getAsCfAccessFlags())
+ .withItem(DexEncodedMethod::proto)
+ .withCustomItem(
+ DexEncodedMethod::getCode,
+ (c1, c2, v) -> v.visit(c1, c2, DexEncodedMethod::compareCodeObject),
+ (c, h) -> h.visit(c, DexEncodedMethod::hashCodeObject));
+ }
+
+ public void hashSyntheticContent(Hasher hasher, RepresentativeMap map) {
+ HashingVisitorWithTypeEquivalence.run(this, hasher, map, DexEncodedMethod::syntheticSpecify);
+ }
+
+ public boolean isSyntheticContentEqual(DexEncodedMethod other) {
+ return syntheticCompareTo(other) == 0;
+ }
+
+ public int syntheticCompareTo(DexEncodedMethod other) {
+ // Consider the holder types to be equivalent, using the holder of this method as the
+ // representative.
+ RepresentativeMap map = t -> t == other.getHolderType() ? getHolderType() : t;
+ return CompareToVisitorWithTypeEquivalence.run(
+ this, other, map, DexEncodedMethod::syntheticSpecify);
+ }
+
+ private static int compareCodeObject(Code code1, Code code2) {
+ if (code1.isCfCode() && code2.isCfCode()) {
+ return code1.asCfCode().compareTo(code2.asCfCode());
+ }
+ if (code1.isDexCode() && code2.isDexCode()) {
+ return code1.asDexCode().compareTo(code2.asDexCode());
+ }
+ throw new Unreachable(
+ "Unexpected attempt to compare incompatible synthetic objects: " + code1 + " and " + code2);
+ }
+
+ private static void hashCodeObject(Code code, Hasher hasher) {
// TODO(b/158159959): Implement a more precise hashing on code objects.
if (code.isCfCode()) {
CfCode cfCode = code.asCfCode();
@@ -339,30 +379,6 @@
}
}
- public boolean isSyntheticContentEqual(DexEncodedMethod other) {
- return syntheticCompareTo(other) == 0;
- }
-
- public int syntheticCompareTo(DexEncodedMethod other) {
- assert annotations().isEmpty();
- assert parameterAnnotationsList.isEmpty();
- Comparator<DexEncodedMethod> comparator =
- Comparator.comparing(DexEncodedMethod::proto, DexProto::slowCompareTo)
- .thenComparingInt(m -> m.accessFlags.getAsCfAccessFlags());
- if (code.isCfCode() && other.getCode().isCfCode()) {
- comparator = comparator.thenComparing(m -> m.getCode().asCfCode());
- } else if (code.isDexCode() && other.getCode().isDexCode()) {
- comparator = comparator.thenComparing(m -> m.getCode().asDexCode());
- } else {
- throw new Unreachable(
- "Unexpected attempt to compare incompatible synthetic objects: "
- + code
- + " and "
- + other.getCode());
- }
- return comparator.compare(this, other);
- }
-
public DexType getHolderType() {
return getReference().holder;
}
@@ -832,7 +848,7 @@
public void upgradeClassFileVersion(CfVersion version) {
checkIfObsolete();
assert version != null;
- classFileVersion = CfVersion.maxAllowNull(classFileVersion, version);
+ classFileVersion = Ordered.maxIgnoreNull(classFileVersion, version);
}
public String qualifiedName() {
@@ -1216,7 +1232,7 @@
public DexEncodedMethod toRenamedHolderMethod(DexType newHolderType, DexItemFactory factory) {
DexEncodedMethod.Builder builder = DexEncodedMethod.builder(this);
- builder.setMethod(factory.createMethod(newHolderType, method.proto, method.name));
+ builder.setMethod(method.withHolder(newHolderType, factory));
return builder.build();
}
@@ -1282,8 +1298,7 @@
// and if different forwarding methods are created in different subclasses the first could be
// final.
accessFlags.demoteFromFinal();
- DexMethod newMethod =
- definitions.dexItemFactory().createMethod(holder.type, method.proto, method.name);
+ DexMethod newMethod = method.withHolder(holder.type, definitions.dexItemFactory());
Invoke.Type type = accessFlags.isStatic() ? Invoke.Type.STATIC : Invoke.Type.SUPER;
Builder builder = syntheticBuilder(this);
builder.setMethod(newMethod);
@@ -1417,7 +1432,7 @@
}
public static int slowCompare(DexEncodedMethod m1, DexEncodedMethod m2) {
- return m1.method.slowCompareTo(m2.method);
+ return m1.method.compareTo(m2.method);
}
public MethodOptimizationInfo getOptimizationInfo() {
@@ -1548,6 +1563,11 @@
return this;
}
+ public Builder setIsLibraryMethodOverrideIfKnown(OptionalBool isLibraryMethodOverride) {
+ return setIsLibraryMethodOverrideIf(
+ !isLibraryMethodOverride.isUnknown(), isLibraryMethodOverride);
+ }
+
public Builder setParameterAnnotations(ParameterAnnotationsList parameterAnnotations) {
this.parameterAnnotations = parameterAnnotations;
return this;
diff --git a/src/main/java/com/android/tools/r8/graph/DexField.java b/src/main/java/com/android/tools/r8/graph/DexField.java
index 87adbf7..5f95369 100644
--- a/src/main/java/com/android/tools/r8/graph/DexField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -5,9 +5,11 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import java.util.Collections;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -26,10 +28,28 @@
}
}
+ private static void accept(StructuralSpecification<DexField, ?> spec) {
+ spec.withItem(DexField::getHolderType).withItem(DexField::getName).withItem(DexField::getType);
+ }
+
+ @Override
+ public DexField self() {
+ return this;
+ }
+
+ @Override
+ public StructuralAccept<DexField> getStructuralAccept() {
+ return DexField::accept;
+ }
+
public DexType getHolderType() {
return holder;
}
+ public DexString getName() {
+ return name;
+ }
+
public DexType getType() {
return type;
}
@@ -122,29 +142,8 @@
}
@Override
- public int slowCompareTo(DexField other) {
- int result = holder.slowCompareTo(other.holder);
- if (result != 0) {
- return result;
- }
- result = name.slowCompareTo(other.name);
- if (result != 0) {
- return result;
- }
- return type.slowCompareTo(other.type);
- }
-
- @Override
- public int slowCompareTo(DexField other, NamingLens namingLens) {
- int result = holder.slowCompareTo(other.holder, namingLens);
- if (result != 0) {
- return result;
- }
- result = namingLens.lookupName(this).slowCompareTo(namingLens.lookupName(other));
- if (result != 0) {
- return result;
- }
- return type.slowCompareTo(other.type, namingLens);
+ public void acceptCompareTo(DexField other, CompareToVisitor visitor) {
+ visitor.visitDexField(this, other);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 6edd1e1..eee5f4b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -1784,6 +1784,7 @@
Function<DexString, Optional<T>> tryString, String baseName, DexType holder) {
int index = 0;
while (true) {
+ assert index < 1000;
DexString name = createString(createMemberString(baseName, holder, index++));
Optional<T> result = tryString.apply(name);
if (result.isPresent()) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
index dcb4ed3..e5a8097 100644
--- a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
@@ -12,6 +12,8 @@
import com.android.tools.r8.origin.Origin;
import java.util.Arrays;
import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
import java.util.function.Supplier;
public class DexLibraryClass extends DexClass implements Supplier<DexLibraryClass> {
@@ -122,4 +124,18 @@
public DexLibraryClass get() {
return this;
}
+
+ @Override
+ boolean internalClassOrInterfaceMayHaveInitializationSideEffects(
+ AppView<?> appView,
+ DexClass initialAccessHolder,
+ Predicate<DexType> ignore,
+ Set<DexType> seen) {
+ if (!seen.add(getType()) || ignore.test(getType())) {
+ return false;
+ }
+ return isInterface()
+ ? appView.options().libraryInterfacesMayHaveStaticInitialization
+ : !appView.dexItemFactory().libraryClassesWithoutStaticInitialization.contains(type);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexMember.java b/src/main/java/com/android/tools/r8/graph/DexMember.java
index 3041e01..84a1953 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMember.java
@@ -6,7 +6,7 @@
import com.google.common.collect.Iterables;
public abstract class DexMember<D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
- extends DexReference implements PresortedComparable<R> {
+ extends DexReference implements NamingLensComparable<R> {
public final DexType holder;
public final DexString name;
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index 6c288c7..249e77e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -5,10 +5,12 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
@@ -29,6 +31,25 @@
}
}
+ private static void accept(StructuralSpecification<DexMethod, ?> spec) {
+ spec.withItem(DexMethod::getHolderType).withItem(DexMethod::getName).withItem(m -> m.proto);
+ }
+
+ @Override
+ public StructuralAccept<DexMethod> getStructuralAccept() {
+ return DexMethod::accept;
+ }
+
+ @Override
+ public DexMethod self() {
+ return this;
+ }
+
+ @Override
+ public void acceptCompareTo(DexMethod other, CompareToVisitor visitor) {
+ visitor.visitDexMethod(this, other);
+ }
+
public DexType getHolderType() {
return holder;
}
@@ -181,32 +202,6 @@
}
@Override
- public int slowCompareTo(DexMethod other) {
- int result = holder.slowCompareTo(other.holder);
- if (result != 0) {
- return result;
- }
- result = name.slowCompareTo(other.name);
- if (result != 0) {
- return result;
- }
- return proto.slowCompareTo(other.proto);
- }
-
- @Override
- public int slowCompareTo(DexMethod other, NamingLens namingLens) {
- int result = holder.slowCompareTo(other.holder, namingLens);
- if (result != 0) {
- return result;
- }
- result = namingLens.lookupName(this).slowCompareTo(namingLens.lookupName(other));
- if (result != 0) {
- return result;
- }
- return proto.slowCompareTo(other.proto, namingLens);
- }
-
- @Override
public boolean match(DexMethod method) {
return method.name == name && method.proto == proto;
}
@@ -270,8 +265,12 @@
holder, dexItemFactory.prependTypeToProto(type, proto), name);
}
- public DexMethod withHolder(DexType holder, DexItemFactory dexItemFactory) {
- return dexItemFactory.createMethod(holder, proto, name);
+ public DexMethod withHolder(DexDefinition definition, DexItemFactory dexItemFactory) {
+ return withHolder(definition.getReference(), dexItemFactory);
+ }
+
+ public DexMethod withHolder(DexReference reference, DexItemFactory dexItemFactory) {
+ return dexItemFactory.createMethod(reference.getContextType(), proto, name);
}
public DexMethod withName(DexString name, DexItemFactory dexItemFactory) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
index 80a3cb9..5ee9c8f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -8,12 +8,14 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import java.util.Objects;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;
-public class DexMethodHandle extends IndexedDexItem implements
- PresortedComparable<DexMethodHandle> {
+public class DexMethodHandle extends IndexedDexItem
+ implements NamingLensComparable<DexMethodHandle> {
public enum MethodHandleType {
STATIC_PUT((short) 0x00),
@@ -313,31 +315,21 @@
}
@Override
- public int slowCompareTo(DexMethodHandle other) {
- int result = type.getValue() - other.type.getValue();
- if (result == 0) {
- if (isFieldHandle()) {
- result = asField().slowCompareTo(other.asField());
- } else {
- assert isMethodHandle();
- result = asMethod().slowCompareTo(other.asMethod());
- }
- }
- return result;
+ public DexMethodHandle self() {
+ return this;
}
@Override
- public int slowCompareTo(DexMethodHandle other, NamingLens namingLens) {
- int result = type.getValue() - other.type.getValue();
- if (result == 0) {
- if (isFieldHandle()) {
- result = asField().slowCompareTo(other.asField(), namingLens);
- } else {
- assert isMethodHandle();
- result = asMethod().slowCompareTo(other.asMethod(), namingLens);
- }
- }
- return result;
+ public StructuralAccept<DexMethodHandle> getStructuralAccept() {
+ return DexMethodHandle::specify;
+ }
+
+ private static void specify(StructuralSpecification<DexMethodHandle, ?> spec) {
+ spec.withInt(m -> m.type.getValue())
+ .withConditionalItem(DexMethodHandle::isFieldHandle, DexMethodHandle::asField)
+ .withConditionalItem(DexMethodHandle::isMethodHandle, DexMethodHandle::asMethod)
+ .withBool(m -> m.isInterface)
+ .withItem(m -> m.rewrittenTarget);
}
public Handle toAsmHandle(NamingLens lens) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 6474140..ee32f8a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -29,6 +29,7 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -402,6 +403,102 @@
this.kotlinInfo = kotlinInfo;
}
+ @Override
+ boolean internalClassOrInterfaceMayHaveInitializationSideEffects(
+ AppView<?> appView,
+ DexClass initialAccessHolder,
+ Predicate<DexType> ignore,
+ Set<DexType> seen) {
+ if (!seen.add(getType()) || ignore.test(getType())) {
+ return false;
+ }
+ return isInterface()
+ ? internalInterfaceMayHaveInitializationSideEffects(
+ appView, initialAccessHolder, ignore, seen)
+ : internalClassMayHaveInitializationSideEffects(appView, initialAccessHolder, ignore, seen);
+ }
+
+ private boolean internalClassMayHaveInitializationSideEffects(
+ AppView<?> appView,
+ DexClass initialAccessHolder,
+ Predicate<DexType> ignore,
+ Set<DexType> seen) {
+ assert !isInterface();
+ assert seen.contains(getType());
+ assert !ignore.test(getType());
+ if (hasClassInitializer()
+ && !getClassInitializer().getOptimizationInfo().classInitializerMayBePostponed()) {
+ return true;
+ }
+ return defaultValuesForStaticFieldsMayTriggerAllocation()
+ || initializationOfParentTypesMayHaveSideEffects(
+ appView, initialAccessHolder, ignore, seen);
+ }
+
+ /**
+ * Interface initialization is described the JVM Specification, section 5.5 Initialization (Java
+ * SE 11 Edition).
+ *
+ * <p>A class or interface C may be initialized only as a result of:
+ *
+ * <ul>
+ * <li>The execution of any one of the Java Virtual Machine instructions new, getstatic,
+ * putstatic, or invokestatic that references C.
+ * <li>...
+ * <li>If C is an interface that declares a non-abstract, non-static method, the initialization
+ * of a class that implements C directly or indirectly.
+ * </ul>
+ */
+ private boolean internalInterfaceMayHaveInitializationSideEffects(
+ AppView<?> appView,
+ DexClass initialAccessHolder,
+ Predicate<DexType> ignore,
+ Set<DexType> seen) {
+ assert isInterface();
+ assert seen.contains(getType());
+ assert !ignore.test(getType());
+
+ // If there is a direct access to the interface, then this has side effects if its clinit has
+ // side effects. Parent types are not initialized and thus don't need to be considered.
+ if (this == initialAccessHolder) {
+ if (hasClassInitializer()
+ && !getClassInitializer().getOptimizationInfo().classInitializerMayBePostponed()) {
+ return true;
+ }
+ return defaultValuesForStaticFieldsMayTriggerAllocation();
+ }
+
+ // Otherwise, this interface has side effects if its clinit has side effects and it has at least
+ // one default interface method, or if one of its parent types have observable side effects.
+ if (hasClassInitializer()
+ && !getClassInitializer().getOptimizationInfo().classInitializerMayBePostponed()
+ && getMethodCollection().hasVirtualMethods(DexEncodedMethod::isDefaultMethod)) {
+ return true;
+ }
+
+ return initializationOfParentTypesMayHaveSideEffects(
+ appView, initialAccessHolder, ignore, seen);
+ }
+
+ private boolean initializationOfParentTypesMayHaveSideEffects(
+ AppView<?> appView,
+ DexClass initialAccessHolder,
+ Predicate<DexType> ignore,
+ Set<DexType> seen) {
+ if (superType != null
+ && superType.internalClassOrInterfaceMayHaveInitializationSideEffects(
+ appView, initialAccessHolder, ignore, seen)) {
+ return true;
+ }
+ for (DexType iface : interfaces) {
+ if (iface.internalClassOrInterfaceMayHaveInitializationSideEffects(
+ appView, initialAccessHolder, ignore, seen)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public boolean hasFields() {
return instanceFields.length + staticFields.length > 0;
}
@@ -454,7 +551,7 @@
return null;
}
DexEncodedField[] fields = staticFields;
- Arrays.sort(fields, (a, b) -> a.field.slowCompareTo(b.field, namingLens));
+ Arrays.sort(fields, (a, b) -> a.field.compareToWithNamingLens(b.field, namingLens));
int length = 0;
List<DexValue> values = new ArrayList<>(fields.length);
for (int i = 0; i < fields.length; i++) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexProto.java b/src/main/java/com/android/tools/r8/graph/DexProto.java
index 333be20..3855408 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProto.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProto.java
@@ -5,12 +5,13 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
import com.google.common.collect.Iterables;
-import com.google.common.hash.Hasher;
import java.util.Collections;
import java.util.function.Consumer;
-public class DexProto extends IndexedDexItem implements PresortedComparable<DexProto> {
+public class DexProto extends IndexedDexItem implements NamingLensComparable<DexProto> {
public static final DexProto SENTINEL = new DexProto(null, null, null);
@@ -24,6 +25,43 @@
this.parameters = parameters;
}
+ private static void accept(StructuralSpecification<DexProto, ?> spec) {
+ spec.withItem(DexProto::getReturnType)
+ .withItem(p -> p.parameters)
+ // TODO(b/172206529): Consider removing shorty.
+ .withItem(p1 -> p1.shorty);
+ }
+
+ @Override
+ public StructuralAccept<DexProto> getStructuralAccept() {
+ return DexProto::accept;
+ }
+
+ @Override
+ public DexProto self() {
+ return this;
+ }
+
+ @Override
+ public boolean computeEquals(Object other) {
+ if (other instanceof DexProto) {
+ DexProto o = (DexProto) other;
+ return shorty.equals(o.shorty)
+ && returnType.equals(o.returnType)
+ && parameters.equals(o.parameters);
+ }
+ return false;
+ }
+
+ @Override
+ public int computeHashCode() {
+ return shorty.hashCode() + returnType.hashCode() * 7 + parameters.hashCode() * 31;
+ }
+
+ public DexType getReturnType() {
+ return returnType;
+ }
+
public Iterable<DexType> getParameterBaseTypes(DexItemFactory dexItemFactory) {
return Iterables.transform(parameters, type -> type.toBaseType(dexItemFactory));
}
@@ -50,24 +88,6 @@
}
@Override
- public int computeHashCode() {
- return shorty.hashCode()
- + returnType.hashCode() * 7
- + parameters.hashCode() * 31;
- }
-
- @Override
- public boolean computeEquals(Object other) {
- if (other instanceof DexProto) {
- DexProto o = (DexProto) other;
- return shorty.equals(o.shorty)
- && returnType.equals(o.returnType)
- && parameters.equals(o.parameters);
- }
- return false;
- }
-
- @Override
public String toString() {
return "Proto " + shorty + " " + returnType + " " + parameters;
}
@@ -86,24 +106,6 @@
}
@Override
- public int slowCompareTo(DexProto other) {
- int result = returnType.slowCompareTo(other.returnType);
- if (result == 0) {
- result = parameters.slowCompareTo(other.parameters);
- }
- return result;
- }
-
- @Override
- public int slowCompareTo(DexProto other, NamingLens namingLens) {
- int result = returnType.slowCompareTo(other.returnType, namingLens);
- if (result == 0) {
- result = parameters.slowCompareTo(other.parameters, namingLens);
- }
- return result;
- }
-
- @Override
public String toSmaliString() {
return toDescriptorString();
}
@@ -122,11 +124,4 @@
builder.append(lens.lookupDescriptor(returnType));
return builder.toString();
}
-
- public void hashSyntheticContent(Hasher hasher) {
- hasher.putInt(returnType.hashCode());
- for (DexType param : parameters.values) {
- hasher.putInt(param.hashCode());
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexReference.java b/src/main/java/com/android/tools/r8/graph/DexReference.java
index 3263b6d..d32c7ba 100644
--- a/src/main/java/com/android/tools/r8/graph/DexReference.java
+++ b/src/main/java/com/android/tools/r8/graph/DexReference.java
@@ -80,12 +80,12 @@
return typeDiff;
}
if (isDexType()) {
- return asDexType().slowCompareTo(o.asDexType());
+ return asDexType().compareTo(o.asDexType());
}
if (isDexField()) {
- return asDexField().slowCompareTo(o.asDexField());
+ return asDexField().compareTo(o.asDexField());
}
assert isDexMethod();
- return asDexMethod().slowCompareTo(o.asDexMethod());
+ return asDexMethod().compareTo(o.asDexMethod());
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexString.java b/src/main/java/com/android/tools/r8/graph/DexString.java
index b5d50f0..7a50fe1 100644
--- a/src/main/java/com/android/tools/r8/graph/DexString.java
+++ b/src/main/java/com/android/tools/r8/graph/DexString.java
@@ -5,16 +5,19 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.IdentifierUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThrowingCharIterator;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
+import com.android.tools.r8.utils.structural.StructuralAccept;
import java.io.UTFDataFormatException;
import java.util.Arrays;
import java.util.NoSuchElementException;
-public class DexString extends IndexedDexItem implements PresortedComparable<DexString> {
+public class DexString extends IndexedDexItem implements NamingLensComparable<DexString> {
public static final DexString[] EMPTY_ARRAY = {};
private static final int ARRAY_CHARACTER = '[';
@@ -32,6 +35,27 @@
this.content = encodeToMutf8(string);
}
+ @Override
+ public DexString self() {
+ return this;
+ }
+
+ @Override
+ public StructuralAccept<DexString> getStructuralAccept() {
+ // Structural accept is never accessed as all accept methods are defined directly.
+ throw new Unreachable();
+ }
+
+ @Override
+ public void acceptCompareTo(DexString other, CompareToVisitor visitor) {
+ visitor.visitDexString(this, other, DexString::internalCompareTo);
+ }
+
+ @Override
+ public void acceptHashing(HashingVisitor visitor) {
+ visitor.visitDexString(this);
+ }
+
public ThrowingCharIterator<UTFDataFormatException> iterator() {
return new ThrowingCharIterator<UTFDataFormatException>() {
@@ -103,14 +127,6 @@
}
}
- public int numberOfLeadingSquareBrackets() {
- int result = 0;
- while (content.length > result && content[result] == ((byte) '[')) {
- result++;
- }
- return result;
- }
-
private String decode() throws UTFDataFormatException {
char[] out = new char[size];
int decodedLength = decodePrefix(out);
@@ -248,8 +264,7 @@
return mapping.getOffsetFor(this);
}
- @Override
- public int slowCompareTo(DexString other) {
+ private int internalCompareTo(DexString other) {
// Compare the bytes, as comparing UTF-8 encoded strings as strings of unsigned bytes gives
// the same result as comparing the corresponding Unicode strings lexicographically by
// codepoint. The only complication is the MUTF-8 encoding have the two byte encoding c0 80 of
@@ -281,12 +296,6 @@
}
}
- @Override
- public int slowCompareTo(DexString other, NamingLens lens) {
- // The naming lens cannot affect strings.
- return slowCompareTo(other);
- }
-
private static boolean isValidClassDescriptor(String string) {
if (string.length() < 3
|| string.charAt(0) != 'L'
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 72e632d..39aea8c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -20,14 +20,14 @@
import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring;
import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
import com.android.tools.r8.ir.optimize.ServiceLoaderRewriter;
-import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
-import com.google.common.base.Predicates;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
+import com.android.tools.r8.utils.structural.StructuralAccept;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Sets;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
@@ -36,7 +36,7 @@
import java.util.function.Function;
import java.util.function.Predicate;
-public class DexType extends DexReference implements PresortedComparable<DexType> {
+public class DexType extends DexReference implements NamingLensComparable<DexType> {
public static final DexType[] EMPTY_ARRAY = {};
// Bundletool is merging classes that may originate from a build with an old version of R8.
@@ -53,6 +53,29 @@
}
@Override
+ public DexType self() {
+ return this;
+ }
+
+ @Override
+ public StructuralAccept<DexType> getStructuralAccept() {
+ // Structural accept is never accessed as all accept methods are defined directly.
+ throw new Unreachable();
+ }
+
+ // DexType overrides accept to ensure the visitors always gets a visitDexType callback.
+ @Override
+ public void acceptCompareTo(DexType other, CompareToVisitor visitor) {
+ visitor.visitDexType(this, other);
+ }
+
+ // DexType overrides accept to ensure the visitors always gets a visitDexType callback.
+ @Override
+ public void acceptHashing(HashingVisitor visitor) {
+ visitor.visitDexType(this);
+ }
+
+ @Override
public DexType getContextType() {
return this;
}
@@ -74,27 +97,21 @@
return false;
}
- public boolean classInitializationMayHaveSideEffects(AppView<?> appView) {
- return classInitializationMayHaveSideEffects(
- appView, Predicates.alwaysFalse(), Sets.newIdentityHashSet());
- }
-
- public boolean classInitializationMayHaveSideEffects(
- AppView<?> appView, Predicate<DexType> ignore, Set<DexType> seen) {
+ public boolean classInitializationMayHaveSideEffectsInContext(
+ AppView<?> appView, ProgramDefinition context) {
DexClass clazz = appView.definitionFor(this);
- return clazz == null || clazz.classInitializationMayHaveSideEffects(appView, ignore, seen);
+ return clazz == null || clazz.classInitializationMayHaveSideEffectsInContext(appView, context);
}
- public boolean initializationOfParentTypesMayHaveSideEffects(AppView<?> appView) {
- return initializationOfParentTypesMayHaveSideEffects(
- appView, Predicates.alwaysFalse(), Sets.newIdentityHashSet());
- }
-
- public boolean initializationOfParentTypesMayHaveSideEffects(
- AppView<?> appView, Predicate<DexType> ignore, Set<DexType> seen) {
+ final boolean internalClassOrInterfaceMayHaveInitializationSideEffects(
+ AppView<?> appView,
+ DexClass initialAccessHolder,
+ Predicate<DexType> ignore,
+ Set<DexType> seen) {
DexClass clazz = appView.definitionFor(this);
return clazz == null
- || clazz.initializationOfParentTypesMayHaveSideEffects(appView, ignore, seen);
+ || clazz.internalClassOrInterfaceMayHaveInitializationSideEffects(
+ appView, initialAccessHolder, ignore, seen);
}
public boolean isAlwaysNull(AppView<AppInfoWithLiveness> appView) {
@@ -209,18 +226,6 @@
return this;
}
- @Override
- public int slowCompareTo(DexType other) {
- return descriptor.slowCompareTo(other.descriptor);
- }
-
- @Override
- public int slowCompareTo(DexType other, NamingLens namingLens) {
- DexString thisDescriptor = namingLens.lookupDescriptor(this);
- DexString otherDescriptor = namingLens.lookupDescriptor(other);
- return thisDescriptor.slowCompareTo(otherDescriptor, namingLens);
- }
-
public boolean isPrimitiveType() {
return DescriptorUtils.isPrimitiveType((char) descriptor.content[0]);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeList.java b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
index 7d6ed51..a641039 100644
--- a/src/main/java/com/android/tools/r8/graph/DexTypeList.java
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
@@ -6,15 +6,18 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.ArrayUtils;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
+import com.android.tools.r8.utils.structural.StructuralAccept;
+import com.android.tools.r8.utils.structural.StructuralItem;
import com.google.common.collect.Iterators;
import java.util.Arrays;
import java.util.Iterator;
import java.util.function.Consumer;
import java.util.stream.Stream;
-public class DexTypeList extends DexItem implements Iterable<DexType> {
+public class DexTypeList extends DexItem implements Iterable<DexType>, StructuralItem<DexTypeList> {
private static final DexTypeList theEmptyTypeList = new DexTypeList();
@@ -33,6 +36,27 @@
this.values = values;
}
+ @Override
+ public StructuralAccept<DexTypeList> getStructuralAccept() {
+ // Structural accept is never accessed as all accept methods are defined directly.
+ throw new Unreachable();
+ }
+
+ @Override
+ public DexTypeList self() {
+ return this;
+ }
+
+ @Override
+ public void acceptCompareTo(DexTypeList other, CompareToVisitor visitor) {
+ visitor.visitDexTypeList(this, other);
+ }
+
+ @Override
+ public void acceptHashing(HashingVisitor visitor) {
+ visitor.visitDexTypeList(this);
+ }
+
public boolean contains(DexType type) {
return ArrayUtils.contains(values, type);
}
@@ -93,38 +117,6 @@
return builder.toString();
}
- public int slowCompareTo(DexTypeList other) {
- for (int i = 0; i <= Math.min(values.length, other.values.length); i++) {
- if (i == values.length) {
- return i == other.values.length ? 0 : -1;
- } else if (i == other.values.length) {
- return 1;
- } else {
- int result = values[i].slowCompareTo(other.values[i]);
- if (result != 0) {
- return result;
- }
- }
- }
- throw new Unreachable();
- }
-
- public int slowCompareTo(DexTypeList other, NamingLens namingLens) {
- for (int i = 0; i <= Math.min(values.length, other.values.length); i++) {
- if (i == values.length) {
- return i == other.values.length ? 0 : -1;
- } else if (i == other.values.length) {
- return 1;
- } else {
- int result = values[i].slowCompareTo(other.values[i], namingLens);
- if (result != 0) {
- return result;
- }
- }
- }
- throw new Unreachable();
- }
-
@Override
public Iterator<DexType> iterator() {
return Iterators.forArray(values);
@@ -136,7 +128,7 @@
}
DexType[] newValues = values.clone();
- Arrays.sort(newValues, DexType::slowCompareTo);
+ Arrays.sort(newValues);
return new DexTypeList(newValues);
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
index 88869df..439ef05 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.graph;
+import com.android.tools.r8.utils.ObjectUtils;
import com.android.tools.r8.utils.SetUtils;
import java.util.IdentityHashMap;
import java.util.Map;
@@ -75,10 +76,16 @@
public FieldAccessInfoCollectionImpl rewrittenWithLens(
DexDefinitionSupplier definitions, GraphLens lens) {
FieldAccessInfoCollectionImpl collection = new FieldAccessInfoCollectionImpl();
- infos.forEach(
- (field, info) ->
- collection.infos.put(
- lens.lookupField(field), info.rewrittenWithLens(definitions, lens)));
+ Consumer<FieldAccessInfoImpl> rewriteAndMergeFieldInfo =
+ info -> {
+ FieldAccessInfoImpl rewrittenInfo = info.rewrittenWithLens(definitions, lens);
+ DexField newField = rewrittenInfo.getField();
+ collection.infos.compute(
+ newField,
+ (ignore, oldInfo) ->
+ ObjectUtils.mapNotNullOrDefault(oldInfo, rewrittenInfo, rewrittenInfo::join));
+ };
+ infos.values().forEach(rewriteAndMergeFieldInfo);
return collection;
}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
index 9dae761..641ef08 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
@@ -281,4 +281,12 @@
rewritten.writesWithContexts = writesWithContexts.rewrittenWithLens(definitions, lens);
return rewritten;
}
+
+ public FieldAccessInfoImpl join(FieldAccessInfoImpl impl) {
+ FieldAccessInfoImpl merged = new FieldAccessInfoImpl(field);
+ merged.flags = flags | impl.flags;
+ merged.readsWithContexts = readsWithContexts.join(impl.readsWithContexts);
+ merged.writesWithContexts = writesWithContexts.join(impl.writesWithContexts);
+ return merged;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
index e9b45f1..fd77b73 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
@@ -10,6 +10,7 @@
import static com.android.tools.r8.graph.GenericSignature.EMPTY_TYPE_SIGNATURES;
import static com.android.tools.r8.graph.GenericSignature.FieldTypeSignature.noSignature;
import static com.android.tools.r8.graph.GenericSignature.StarFieldTypeSignature.STAR_FIELD_TYPE_SIGNATURE;
+import static com.google.common.base.Predicates.alwaysFalse;
import com.android.tools.r8.graph.GenericSignature.ArrayTypeSignature;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
@@ -19,22 +20,40 @@
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.GenericSignature.ReturnType;
import com.android.tools.r8.graph.GenericSignature.TypeSignature;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Function;
+import java.util.function.Predicate;
public class GenericSignatureTypeRewriter {
- private final AppView<?> appView;
+ private final DexItemFactory factory;
+ private final Predicate<DexType> wasPruned;
+ private final Function<DexType, DexType> lookupType;
private final DexProgramClass context;
private final FieldTypeSignature objectTypeSignature;
public GenericSignatureTypeRewriter(AppView<?> appView, DexProgramClass context) {
- this.appView = appView;
+ this(
+ appView.dexItemFactory(),
+ appView.appInfo().hasLiveness()
+ ? appView.appInfo().withLiveness()::wasPruned
+ : alwaysFalse(),
+ appView.graphLens()::lookupType,
+ context);
+ }
+
+ public GenericSignatureTypeRewriter(
+ DexItemFactory factory,
+ Predicate<DexType> wasPruned,
+ Function<DexType, DexType> lookupType,
+ DexProgramClass context) {
+ this.factory = factory;
+ this.wasPruned = wasPruned;
+ this.lookupType = lookupType;
this.context = context;
- objectTypeSignature =
- new ClassTypeSignature(appView.dexItemFactory().objectType, EMPTY_TYPE_ARGUMENTS);
+ objectTypeSignature = new ClassTypeSignature(factory.objectType, EMPTY_TYPE_ARGUMENTS);
}
public ClassSignature rewrite(ClassSignature classSignature) {
@@ -48,7 +67,8 @@
if (fieldTypeSignature.hasNoSignature()) {
return fieldTypeSignature;
}
- return new TypeSignatureRewriter().run(fieldTypeSignature);
+ FieldTypeSignature rewrittenSignature = new TypeSignatureRewriter().run(fieldTypeSignature);
+ return rewrittenSignature == null ? FieldTypeSignature.noSignature() : rewrittenSignature;
}
public MethodTypeSignature rewrite(MethodTypeSignature methodTypeSignature) {
@@ -80,8 +100,7 @@
public void visitSuperClass(ClassTypeSignature classTypeSignature) {
rewrittenSuperClass = new ClassTypeSignatureRewriter(true).run(classTypeSignature);
if (rewrittenSuperClass == null) {
- rewrittenSuperClass =
- new ClassTypeSignature(appView.dexItemFactory().objectType, EMPTY_TYPE_ARGUMENTS);
+ rewrittenSuperClass = new ClassTypeSignature(factory.objectType, EMPTY_TYPE_ARGUMENTS);
}
}
@@ -99,7 +118,7 @@
if (rewrittenTypeParameters.isEmpty()
&& rewrittenSuperInterfaces.isEmpty()
&& rewrittenSuperClass.isNoSignature()
- && rewrittenSuperClass.type == appView.dexItemFactory().objectType) {
+ && rewrittenSuperClass.type == factory.objectType) {
return ClassSignature.noSignature();
}
return new ClassSignature(
@@ -245,7 +264,6 @@
private class ClassTypeSignatureRewriter implements GenericSignatureVisitor {
- private final AppInfoWithLiveness appInfoWithLiveness;
private final boolean isSuperClassOrInterface;
// These fields are updated when iterating the modeled structure.
@@ -259,8 +277,6 @@
private ClassTypeSignature parentClassSignature;
private ClassTypeSignatureRewriter(boolean isSuperClassOrInterface) {
- appInfoWithLiveness =
- appView.appInfo().hasLiveness() ? appView.appInfo().withLiveness() : null;
this.isSuperClassOrInterface = isSuperClassOrInterface;
}
@@ -313,8 +329,8 @@
}
private DexType getTarget(DexType type) {
- DexType rewrittenType = appView.graphLens().lookupType(type);
- if (appInfoWithLiveness != null && appInfoWithLiveness.wasPruned(rewrittenType)) {
+ DexType rewrittenType = lookupType.apply(type);
+ if (wasPruned.test(rewrittenType)) {
return null;
}
if (isSuperClassOrInterface && context.type == rewrittenType) {
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index 87cea9c..398ed87 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -19,7 +19,6 @@
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
@@ -29,6 +28,7 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
import java.util.function.Predicate;
/**
@@ -104,22 +104,43 @@
*/
public static class FieldLookupResult extends MemberLookupResult<DexField> {
- private FieldLookupResult(DexField reference, DexField reboundReference) {
+ private final DexType castType;
+
+ private FieldLookupResult(DexField reference, DexField reboundReference, DexType castType) {
super(reference, reboundReference);
+ this.castType = castType;
}
public static Builder builder(GraphLens lens) {
return new Builder(lens);
}
+ public boolean hasCastType() {
+ return castType != null;
+ }
+
+ public DexType getCastType() {
+ return castType;
+ }
+
+ public DexType getRewrittenCastType(Function<DexType, DexType> fn) {
+ return hasCastType() ? fn.apply(castType) : null;
+ }
+
public static class Builder extends MemberLookupResult.Builder<DexField, Builder> {
+ private DexType castType;
private GraphLens lens;
private Builder(GraphLens lens) {
this.lens = lens;
}
+ public Builder setCastType(DexType castType) {
+ this.castType = castType;
+ return this;
+ }
+
@Override
public Builder self() {
return this;
@@ -127,7 +148,7 @@
public FieldLookupResult build() {
// TODO(b/168282032): All non-identity graph lenses should set the rebound reference.
- return new FieldLookupResult(reference, reboundReference);
+ return new FieldLookupResult(reference, reboundReference, castType);
}
}
}
@@ -544,15 +565,6 @@
return builder.build();
}
- public ImmutableSortedSet<DexMethod> rewriteMethodsSorted(Set<DexMethod> methods) {
- ImmutableSortedSet.Builder<DexMethod> builder =
- new ImmutableSortedSet.Builder<>(PresortedComparable::slowCompare);
- for (DexMethod method : methods) {
- builder.add(getRenamedMethodSignature(method));
- }
- return builder.build();
- }
-
public <T> ImmutableMap<DexField, T> rewriteFieldKeys(Map<DexField, T> map) {
ImmutableMap.Builder<DexField, T> builder = ImmutableMap.builder();
map.forEach((field, value) -> builder.put(getRenamedFieldSignature(field), value));
@@ -560,8 +572,7 @@
}
public ImmutableSet<DexType> rewriteTypes(Set<DexType> types) {
- ImmutableSortedSet.Builder<DexType> builder =
- new ImmutableSortedSet.Builder<>(PresortedComparable::slowCompare);
+ ImmutableSet.Builder<DexType> builder = new ImmutableSet.Builder<>();
for (DexType type : types) {
builder.add(lookupType(type));
}
@@ -1056,12 +1067,16 @@
return FieldLookupResult.builder(this)
.setReboundReference(rewrittenReboundReference)
.setReference(rewrittenNonReboundReference)
+ .setCastType(previous.getRewrittenCastType(this::internalDescribeLookupClassType))
.build();
} else {
// TODO(b/168282032): We should always have the rebound reference, so this should become
// unreachable.
DexField rewrittenReference = previous.getRewrittenReference(fieldMap);
- return FieldLookupResult.builder(this).setReference(rewrittenReference).build();
+ return FieldLookupResult.builder(this)
+ .setReference(rewrittenReference)
+ .setCastType(previous.getRewrittenCastType(this::internalDescribeLookupClassType))
+ .build();
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java b/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java
index 8aa36c0..7ba6207 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java
@@ -11,7 +11,6 @@
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
-import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -46,11 +45,6 @@
return new IdentityBuilder();
}
- // TODO(b/132593519): We should not need sorted maps with the new member rebinding analysis.
- public static SortedBuilder sortedBuilder() {
- return new SortedBuilder();
- }
-
public Modifier modifier() {
return new Modifier(
directInvokes, interfaceInvokes, staticInvokes, superInvokes, virtualInvokes);
@@ -103,7 +97,7 @@
Map<DexMethod, ProgramMethodSet> invokes, DexDefinitionSupplier definitions, GraphLens lens) {
return MapUtils.map(
invokes,
- capacity -> new TreeMap<>(DexMethod::slowCompareTo),
+ IdentityHashMap::new,
lens::getRenamedMethodSignature,
methods -> methods.rewrittenWithLens(definitions, lens),
(methods, other) -> {
@@ -225,13 +219,6 @@
}
}
- public static class SortedBuilder extends Builder<TreeMap<DexMethod, ProgramMethodSet>> {
-
- private SortedBuilder() {
- super(() -> new TreeMap<>(DexMethod::slowCompareTo));
- }
- }
-
public static class Modifier extends Builder<Map<DexMethod, ProgramMethodSet>> {
private Modifier(
diff --git a/src/main/java/com/android/tools/r8/graph/MethodCollection.java b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
index d483a59..3200163 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodCollection.java
@@ -137,7 +137,7 @@
public List<DexEncodedMethod> allMethodsSorted() {
List<DexEncodedMethod> sorted = new ArrayList<>(size());
forEachMethod(sorted::add);
- sorted.sort((a, b) -> a.method.slowCompareTo(b.method));
+ sorted.sort((a, b) -> a.method.compareTo(b.method));
return sorted;
}
diff --git a/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java b/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
index 2874e03..c6f7a5f 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
@@ -34,7 +34,7 @@
}
public static MethodMapBacking createSorted() {
- Comparator<Wrapper<DexMethod>> comparator = (x, y) -> x.get().slowCompareTo(y.get());
+ Comparator<Wrapper<DexMethod>> comparator = (x, y) -> x.get().compareTo(y.get());
return new MethodMapBacking(new Object2ReferenceRBTreeMap<>(comparator));
}
diff --git a/src/main/java/com/android/tools/r8/graph/NamingLensComparable.java b/src/main/java/com/android/tools/r8/graph/NamingLensComparable.java
new file mode 100644
index 0000000..e6323eb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/NamingLensComparable.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitorWithNamingLens;
+import com.android.tools.r8.utils.structural.StructuralItem;
+
+public interface NamingLensComparable<T extends NamingLensComparable<T>> extends StructuralItem<T> {
+
+ default int compareToWithNamingLens(T other, NamingLens lens) {
+ return CompareToVisitorWithNamingLens.run(self(), other, lens, StructuralItem::acceptCompareTo);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
index b4797a9..b5ce56a 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
@@ -42,7 +42,7 @@
private DexString firstJumboString;
public ObjectToOffsetMapping(
- AppInfo appInfo,
+ AppView<?> appView,
GraphLens graphLens,
NamingLens namingLens,
InitClassLens initClassLens,
@@ -54,7 +54,7 @@
Collection<DexString> strings,
Collection<DexCallSite> callSites,
Collection<DexMethodHandle> methodHandles) {
- assert appInfo != null;
+ assert appView != null;
assert graphLens != null;
assert classes != null;
assert protos != null;
@@ -68,8 +68,8 @@
this.graphLens = graphLens;
this.namingLens = namingLens;
this.initClassLens = initClassLens;
- this.lensCodeRewriter = new LensCodeRewriterUtils(appInfo, graphLens);
- this.classes = sortClasses(appInfo, classes, namingLens);
+ this.lensCodeRewriter = new LensCodeRewriterUtils(appView);
+ this.classes = sortClasses(appView.appInfo(), classes, namingLens);
this.protos = createSortedMap(protos, compare(namingLens), this::failOnOverflow);
this.types = createSortedMap(types, compare(namingLens), this::failOnOverflow);
this.methods = createSortedMap(methods, compare(namingLens), this::failOnOverflow);
@@ -79,8 +79,8 @@
this.methodHandles = createSortedMap(methodHandles, compare(namingLens), this::failOnOverflow);
}
- private static <T extends PresortedComparable<T>> Comparator<T> compare(NamingLens namingLens) {
- return (a, b) -> a.slowCompareTo(b, namingLens);
+ private static <T extends NamingLensComparable<T>> Comparator<T> compare(NamingLens namingLens) {
+ return (a, b) -> a.compareToWithNamingLens(b, namingLens);
}
private void setFirstJumboString(DexString string) {
@@ -168,7 +168,7 @@
(x, y) -> {
int dx = classDepths.getDepth(x);
int dy = classDepths.getDepth(y);
- return dx != dy ? dx - dy : x.type.slowCompareTo(y.type, namingLens);
+ return dx != dy ? dx - dy : x.type.compareToWithNamingLens(y.type, namingLens);
})
.collect(Collectors.toList());
return sortedClasses.toArray(DexProgramClass.EMPTY_ARRAY);
diff --git a/src/main/java/com/android/tools/r8/graph/PresortedComparable.java b/src/main/java/com/android/tools/r8/graph/PresortedComparable.java
deleted file mode 100644
index e4eb4b7..0000000
--- a/src/main/java/com/android/tools/r8/graph/PresortedComparable.java
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.graph;
-
-import com.android.tools.r8.naming.NamingLens;
-
-public interface PresortedComparable<T> {
-
- int slowCompareTo(T other);
- int slowCompareTo(T other, NamingLens namingLens);
-
- static <T extends PresortedComparable<T>> int slowCompare(T a, T b) {
- return a.slowCompareTo(b);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/graph/SortedProgramPackage.java b/src/main/java/com/android/tools/r8/graph/SortedProgramPackage.java
index 97afd09..08be3b6 100644
--- a/src/main/java/com/android/tools/r8/graph/SortedProgramPackage.java
+++ b/src/main/java/com/android/tools/r8/graph/SortedProgramPackage.java
@@ -9,6 +9,6 @@
public class SortedProgramPackage extends ProgramPackage {
public SortedProgramPackage(String packageDescriptor) {
- super(packageDescriptor, () -> new TreeSet<>((a, b) -> a.getType().slowCompareTo(b.getType())));
+ super(packageDescriptor, () -> new TreeSet<>((a, b) -> a.getType().compareTo(b.getType())));
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java b/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
index b73d2bc..d9b17f2 100644
--- a/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
@@ -280,7 +280,7 @@
private void ensureDirectSubTypeSet() {
if (directSubtypes == NO_DIRECT_SUBTYPE) {
- directSubtypes = new ConcurrentSkipListSet<>(DexType::slowCompareTo);
+ directSubtypes = new ConcurrentSkipListSet<>(DexType::compareTo);
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java
index 285e397..d0948bf 100644
--- a/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java
+++ b/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java
@@ -6,25 +6,40 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.optimize.lambda.LambdaGroup;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.google.common.collect.ImmutableSet;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
+import java.util.Collections;
+import java.util.Map;
import java.util.Set;
+import java.util.function.BiConsumer;
public class HorizontallyMergedLambdaClasses implements MergedClasses {
- private final Set<DexType> sources;
+ private final BidirectionalManyToOneMap<DexType, DexType> mergedClasses;
- public HorizontallyMergedLambdaClasses(Set<DexType> sources) {
- this.sources = sources;
+ public HorizontallyMergedLambdaClasses(Map<DexType, LambdaGroup> lambdas) {
+ this.mergedClasses = new BidirectionalManyToOneMap<>();
+ lambdas.forEach((lambda, group) -> mergedClasses.put(lambda, group.getGroupClassType()));
}
public static HorizontallyMergedLambdaClasses empty() {
- return new HorizontallyMergedLambdaClasses(ImmutableSet.of());
+ return new HorizontallyMergedLambdaClasses(Collections.emptyMap());
+ }
+
+ @Override
+ public void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer) {
+ mergedClasses.forEach(consumer);
+ }
+
+ @Override
+ public boolean hasBeenMerged(DexType type) {
+ return mergedClasses.containsKey(type);
}
@Override
public boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView) {
- for (DexType source : sources) {
+ for (DexType source : mergedClasses.keySet()) {
assert appView.appInfo().wasPruned(source)
: "Expected horizontally merged lambda class `"
+ source.toSourceString()
@@ -32,9 +47,4 @@
}
return true;
}
-
- @Override
- public boolean hasBeenMerged(DexType type) {
- return sources.contains(type);
- }
}
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/MergedClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/MergedClasses.java
index 3f25895..8fa6995 100644
--- a/src/main/java/com/android/tools/r8/graph/classmerging/MergedClasses.java
+++ b/src/main/java/com/android/tools/r8/graph/classmerging/MergedClasses.java
@@ -8,13 +8,17 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.Set;
+import java.util.function.BiConsumer;
public interface MergedClasses {
- boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView);
+ void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer);
boolean hasBeenMerged(DexType type);
+ boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView);
+
/**
* Determine if the class has been merged by the merged classes object. If the merged classes is
* null then return false.
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/MergedClassesCollection.java b/src/main/java/com/android/tools/r8/graph/classmerging/MergedClassesCollection.java
index 55c34b5..1225127 100644
--- a/src/main/java/com/android/tools/r8/graph/classmerging/MergedClassesCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/classmerging/MergedClassesCollection.java
@@ -9,6 +9,8 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
+import java.util.function.BiConsumer;
public class MergedClassesCollection implements MergedClasses {
@@ -19,11 +21,10 @@
}
@Override
- public boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView) {
+ public void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer) {
for (MergedClasses mergedClasses : collection) {
- assert mergedClasses.verifyAllSourcesPruned(appView);
+ mergedClasses.forEachMergeGroup(consumer);
}
- return true;
}
@Override
@@ -35,4 +36,12 @@
}
return false;
}
+
+ @Override
+ public boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView) {
+ for (MergedClasses mergedClasses : collection) {
+ assert mergedClasses.verifyAllSourcesPruned(appView);
+ }
+ return true;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/StaticallyMergedClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/StaticallyMergedClasses.java
new file mode 100644
index 0000000..7046e80
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/classmerging/StaticallyMergedClasses.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph.classmerging;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
+import java.util.Set;
+import java.util.function.BiConsumer;
+
+public class StaticallyMergedClasses implements MergedClasses {
+
+ private final BidirectionalManyToOneMap<DexType, DexType> mergedClasses;
+
+ public StaticallyMergedClasses(BidirectionalManyToOneMap<DexType, DexType> mergedClasses) {
+ this.mergedClasses = mergedClasses;
+ }
+
+ public static StaticallyMergedClasses empty() {
+ return new StaticallyMergedClasses(BidirectionalManyToOneMap.empty());
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer) {
+ mergedClasses.forEach(consumer);
+ }
+
+ @Override
+ public boolean hasBeenMerged(DexType type) {
+ return false;
+ }
+
+ @Override
+ public boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView) {
+ return true;
+ }
+
+ public static class Builder {
+
+ private final BidirectionalManyToOneMap<DexType, DexType> mergedClasses =
+ new BidirectionalManyToOneMap<>();
+
+ private Builder() {}
+
+ public void recordMerge(DexProgramClass source, DexProgramClass target) {
+ for (DexType previousSource : mergedClasses.removeValue(source.getType())) {
+ mergedClasses.put(previousSource, target.getType());
+ }
+ mergedClasses.put(source.getType(), target.getType());
+ }
+
+ public StaticallyMergedClasses build() {
+ return new StaticallyMergedClasses(mergedClasses);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
index ed69ace..4b25311 100644
--- a/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
@@ -7,28 +7,35 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
import java.util.Collection;
-import java.util.Collections;
import java.util.Map;
import java.util.Set;
+import java.util.function.BiConsumer;
public class VerticallyMergedClasses implements MergedClasses {
- private final Map<DexType, DexType> mergedClasses;
- private final Map<DexType, Set<DexType>> mergedClassesInverse;
+ private final BidirectionalManyToOneMap<DexType, DexType> mergedClasses;
- public VerticallyMergedClasses(
- Map<DexType, DexType> mergedClasses, Map<DexType, Set<DexType>> mergedClassesInverse) {
+ public VerticallyMergedClasses(BidirectionalManyToOneMap<DexType, DexType> mergedClasses) {
this.mergedClasses = mergedClasses;
- this.mergedClassesInverse = mergedClassesInverse;
+ }
+
+ public static VerticallyMergedClasses empty() {
+ return new VerticallyMergedClasses(BidirectionalManyToOneMap.empty());
+ }
+
+ @Override
+ public void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer) {
+ mergedClasses.forEach(consumer);
}
public Map<DexType, DexType> getForwardMap() {
- return mergedClasses;
+ return mergedClasses.getForwardMap();
}
public Collection<DexType> getSourcesFor(DexType type) {
- return mergedClassesInverse.getOrDefault(type, Collections.emptySet());
+ return mergedClasses.getKeys(type);
}
public DexType getTargetFor(DexType type) {
@@ -36,27 +43,33 @@
return mergedClasses.get(type);
}
+ public DexType getTargetForOrDefault(DexType type, DexType defaultValue) {
+ return mergedClasses.getOrDefault(type, defaultValue);
+ }
+
public boolean hasBeenMergedIntoSubtype(DexType type) {
return mergedClasses.containsKey(type);
}
+ public boolean isEmpty() {
+ return mergedClasses.isEmpty();
+ }
+
public boolean isTarget(DexType type) {
return !getSourcesFor(type).isEmpty();
}
@Override
- public boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView) {
- for (Collection<DexType> sourcesForTarget : mergedClassesInverse.values()) {
- for (DexType source : sourcesForTarget) {
- assert appView.appInfo().wasPruned(source)
- : "Expected vertically merged class `" + source.toSourceString() + "` to be absent";
- }
- }
- return true;
+ public boolean hasBeenMerged(DexType type) {
+ return hasBeenMergedIntoSubtype(type);
}
@Override
- public boolean hasBeenMerged(DexType type) {
- return hasBeenMergedIntoSubtype(type);
+ public boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView) {
+ for (DexType source : mergedClasses.keySet()) {
+ assert appView.appInfo().wasPruned(source)
+ : "Expected vertically merged class `" + source.toSourceString() + "` to be absent";
+ }
+ return true;
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
new file mode 100644
index 0000000..7cf3029
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.horizontalclassmerging;
+
+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.horizontalclassmerging.HorizontalClassMergerGraphLens.Builder;
+import com.android.tools.r8.utils.ListUtils;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+public class ClassInstanceFieldsMerger {
+
+ // Map from target class field to all fields which should be merged into that field.
+ private final Map<DexEncodedField, List<DexEncodedField>> fieldMappings = new LinkedHashMap<>();
+ private final Builder lensBuilder;
+
+ public ClassInstanceFieldsMerger(
+ HorizontalClassMergerGraphLens.Builder lensBuilder, DexProgramClass target) {
+ this.lensBuilder = lensBuilder;
+ target
+ .instanceFields()
+ .forEach(field -> fieldMappings.computeIfAbsent(field, ignore -> new ArrayList<>()));
+ }
+
+ public void addFields(DexProgramClass clazz) {
+ Map<DexType, List<DexEncodedField>> availableFields = new IdentityHashMap<>();
+ for (DexEncodedField field : fieldMappings.keySet()) {
+ availableFields.computeIfAbsent(field.type(), ignore -> new LinkedList<>()).add(field);
+ }
+
+ for (DexEncodedField oldField : clazz.instanceFields()) {
+ DexEncodedField newField =
+ ListUtils.removeFirstMatch(
+ availableFields.get(oldField.type()),
+ field -> field.getAccessFlags().isSameVisibility(oldField.getAccessFlags()))
+ .get();
+ assert newField != null;
+ fieldMappings.get(newField).add(oldField);
+ }
+ }
+
+ private void mergeField(DexEncodedField oldField, DexEncodedField newField) {
+ if (newField.isFinal() && !oldField.isFinal()) {
+ newField.getAccessFlags().demoteFromFinal();
+ }
+ lensBuilder.moveField(oldField.field, newField.field);
+ }
+
+ private void mergeFields(DexEncodedField newField, Collection<DexEncodedField> oldFields) {
+ DexField newFieldReference = newField.getReference();
+
+ lensBuilder.moveField(newFieldReference, newFieldReference);
+ lensBuilder.setRepresentativeField(newFieldReference, newFieldReference);
+
+ oldFields.forEach(oldField -> mergeField(oldField, newField));
+ }
+
+ public void merge() {
+ fieldMappings.forEach(this::mergeFields);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index 79913ff..f75f969 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.horizontalclassmerging;
+import static com.google.common.base.Predicates.not;
+
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotationSet;
@@ -22,7 +24,6 @@
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
@@ -52,6 +53,7 @@
private final ClassMethodsBuilder classMethodsBuilder = new ClassMethodsBuilder();
private final Reference2IntMap<DexType> classIdentifiers = new Reference2IntOpenHashMap<>();
private final ClassStaticFieldsMerger classStaticFieldsMerger;
+ private final ClassInstanceFieldsMerger classInstanceFieldsMerger;
private final Collection<VirtualMethodMerger> virtualMethodMergers;
private final Collection<ConstructorMerger> constructorMergers;
private final DexField classIdField;
@@ -77,6 +79,7 @@
this.dexItemFactory = appView.dexItemFactory();
this.classStaticFieldsMerger = new ClassStaticFieldsMerger(appView, lensBuilder, target);
+ this.classInstanceFieldsMerger = new ClassInstanceFieldsMerger(lensBuilder, target);
buildClassIdentifierMap();
}
@@ -182,22 +185,26 @@
toMergeGroup.forEach(clazz -> clazz.setStaticFields(null));
}
- void fixFinal() {
- if (Iterables.any(toMergeGroup, Predicates.not(DexProgramClass::isFinal))) {
- target.accessFlags.demoteFromFinal();
+ void fixAccessFlags() {
+ if (Iterables.any(toMergeGroup, not(DexProgramClass::isAbstract))) {
+ target.getAccessFlags().demoteFromAbstract();
+ }
+ if (Iterables.any(toMergeGroup, not(DexProgramClass::isFinal))) {
+ target.getAccessFlags().demoteFromFinal();
}
}
void mergeInstanceFields() {
- // TODO: support instance field merging
- assert Iterables.all(toMergeGroup, clazz -> !clazz.hasInstanceFields());
-
- // The target should only have the class id field.
- assert target.instanceFields().size() == 1;
+ toMergeGroup.forEach(
+ clazz -> {
+ classInstanceFieldsMerger.addFields(clazz);
+ clazz.setInstanceFields(null);
+ });
+ classInstanceFieldsMerger.merge();
}
public void mergeGroup(SyntheticArgumentClass syntheticArgumentClass) {
- fixFinal();
+ fixAccessFlags();
appendClassIdField();
mergeVirtualMethods();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java
index d826b86..e287090 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java
@@ -48,7 +48,7 @@
field = field.toTypeSubstitutedField(newFieldReference);
targetFields.put(newFieldReference, field);
- lensBuilder.mapField(oldFieldReference, newFieldReference);
+ lensBuilder.moveField(oldFieldReference, newFieldReference);
}
public void addFields(DexProgramClass toMerge) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
index d15ed7e..2e2020d 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.structural.Ordered;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
@@ -156,7 +157,7 @@
for (DexEncodedMethod constructor : constructors) {
if (constructor.hasClassFileVersion()) {
classFileVersion =
- CfVersion.maxAllowNull(classFileVersion, constructor.getClassFileVersion());
+ Ordered.maxIgnoreNull(classFileVersion, constructor.getClassFileVersion());
}
DexMethod movedConstructor = moveConstructor(classMethodsBuilder, constructor);
lensBuilder.mapMethod(movedConstructor, movedConstructor);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/FieldMultiset.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/FieldMultiset.java
index 37222de..6cbffcc 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/FieldMultiset.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/FieldMultiset.java
@@ -4,19 +4,59 @@
package com.android.tools.r8.horizontalclassmerging;
+import com.android.tools.r8.graph.AccessFlags;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
-import com.google.common.collect.HashMultiset;
-import com.google.common.collect.Multiset;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
public class FieldMultiset {
- private final Multiset<DexType> fields = HashMultiset.create();
+ private static class VisibilitySignature {
+ private int publicVisible = 0;
+ private int protectedVisible = 0;
+ private int privateVisible = 0;
+ private int packagePrivateVisible = 0;
+
+ public void addAccessModifier(AccessFlags accessFlags) {
+ if (accessFlags.isPublic()) {
+ publicVisible++;
+ } else if (accessFlags.isPrivate()) {
+ privateVisible++;
+ } else if (accessFlags.isPackagePrivate()) {
+ packagePrivateVisible++;
+ } else if (accessFlags.isProtected()) {
+ protectedVisible++;
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ VisibilitySignature that = (VisibilitySignature) o;
+ return publicVisible == that.publicVisible
+ && protectedVisible == that.protectedVisible
+ && privateVisible == that.privateVisible
+ && packagePrivateVisible == that.packagePrivateVisible;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(publicVisible, protectedVisible, privateVisible, packagePrivateVisible);
+ }
+ }
+
+ // This *must* not be an IdentityHashMap, because hash equality does not work for the values.
+ private final Map<DexType, VisibilitySignature> fields = new HashMap<>();
public FieldMultiset(DexProgramClass clazz) {
for (DexEncodedField field : clazz.instanceFields()) {
- fields.add(field.type());
+ fields
+ .computeIfAbsent(field.type(), ignore -> new VisibilitySignature())
+ .addAccessModifier(field.getAccessFlags());
}
}
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 2200267..8dfa67c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -8,19 +8,19 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.horizontalclassmerging.policies.AllInstantiatedOrUninstantiated;
+import com.android.tools.r8.horizontalclassmerging.policies.CheckAbstractClasses;
import com.android.tools.r8.horizontalclassmerging.policies.ClassesHaveSameInterfaces;
import com.android.tools.r8.horizontalclassmerging.policies.DontInlinePolicy;
import com.android.tools.r8.horizontalclassmerging.policies.DontMergeIntoLessVisible;
import com.android.tools.r8.horizontalclassmerging.policies.DontMergeSynchronizedClasses;
import com.android.tools.r8.horizontalclassmerging.policies.IgnoreSynthetics;
-import com.android.tools.r8.horizontalclassmerging.policies.NoAbstractClasses;
import com.android.tools.r8.horizontalclassmerging.policies.NoAnnotations;
import com.android.tools.r8.horizontalclassmerging.policies.NoClassesOrMembersWithAnnotations;
import com.android.tools.r8.horizontalclassmerging.policies.NoEnums;
import com.android.tools.r8.horizontalclassmerging.policies.NoInnerClasses;
-import com.android.tools.r8.horizontalclassmerging.policies.NoInstanceFields;
import com.android.tools.r8.horizontalclassmerging.policies.NoInterfaces;
import com.android.tools.r8.horizontalclassmerging.policies.NoKeepRules;
+import com.android.tools.r8.horizontalclassmerging.policies.NoKotlinLambdas;
import com.android.tools.r8.horizontalclassmerging.policies.NoKotlinMetadata;
import com.android.tools.r8.horizontalclassmerging.policies.NoNativeMethods;
import com.android.tools.r8.horizontalclassmerging.policies.NoRuntimeTypeChecks;
@@ -34,6 +34,7 @@
import com.android.tools.r8.horizontalclassmerging.policies.PreventMethodImplementation;
import com.android.tools.r8.horizontalclassmerging.policies.RespectPackageBoundaries;
import com.android.tools.r8.horizontalclassmerging.policies.SameFeatureSplit;
+import com.android.tools.r8.horizontalclassmerging.policies.SameFields;
import com.android.tools.r8.horizontalclassmerging.policies.SameNestHost;
import com.android.tools.r8.horizontalclassmerging.policies.SameParentClass;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -44,9 +45,8 @@
import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.LinkedHashMap;
+import java.util.Collections;
import java.util.List;
-import java.util.Map;
public class HorizontalClassMerger {
private final AppView<AppInfoWithLiveness> appView;
@@ -61,17 +61,15 @@
DirectMappedDexApplication.Builder appBuilder,
MainDexTracingResult mainDexTracingResult,
RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
- Map<FieldMultiset, List<DexProgramClass>> classes = new LinkedHashMap<>();
+ List<DexProgramClass> initialGroup = appView.appInfo().classesWithDeterministicOrder();
- // Group classes by same field signature using the hash map.
- for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
- classes.computeIfAbsent(new FieldMultiset(clazz), ignore -> new ArrayList<>()).add(clazz);
- }
-
- // Run the policies on all collected classes to produce a final grouping.
+ // Run the policies on all program classes to produce a final grouping.
Collection<List<DexProgramClass>> groups =
new SimplePolicyExecutor()
- .run(classes.values(), getPolicies(mainDexTracingResult, runtimeTypeCheckInfo));
+ .run(
+ Collections.singletonList(initialGroup),
+ getPolicies(mainDexTracingResult, runtimeTypeCheckInfo));
+
// If there are no groups, then end horizontal class merging.
if (groups.isEmpty()) {
appView.setHorizontallyMergedClasses(HorizontallyMergedClasses.empty());
@@ -109,12 +107,12 @@
RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
return ImmutableList.of(
new NotMatchedByNoHorizontalClassMerging(appView),
- new NoInstanceFields(),
+ new SameFields(),
new NoInterfaces(),
new ClassesHaveSameInterfaces(),
new NoAnnotations(),
new NoEnums(appView),
- new NoAbstractClasses(),
+ new CheckAbstractClasses(appView),
new IgnoreSynthetics(appView),
new NoClassesOrMembersWithAnnotations(),
new NoInnerClasses(),
@@ -122,6 +120,7 @@
new NoNativeMethods(),
new NoKeepRules(appView),
new NoKotlinMetadata(),
+ new NoKotlinLambdas(appView),
new NoServiceLoaders(appView),
new NotVerticallyMergedIntoSubtype(appView),
new NoRuntimeTypeChecks(runtimeTypeCheckInfo),
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
index 8dadc71..cc0528f 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.ir.conversion.ExtraParameter;
import com.android.tools.r8.utils.IterableUtils;
import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.IdentityHashMap;
@@ -25,8 +24,9 @@
public class HorizontalClassMergerGraphLens extends NestedGraphLens {
private final AppView<?> appView;
private final Map<DexMethod, List<ExtraParameter>> methodExtraParameters;
- private final Map<DexMethod, DexMethod> originalConstructorSignatures;
+ private final Map<DexMethod, DexMethod> extraOriginalMethodSignatures;
private final HorizontallyMergedClasses mergedClasses;
+ private final Map<DexField, DexField> extraOriginalFieldSignatures;
private HorizontalClassMergerGraphLens(
AppView<?> appView,
@@ -36,7 +36,8 @@
Map<DexMethod, DexMethod> methodMap,
BiMap<DexField, DexField> originalFieldSignatures,
BiMap<DexMethod, DexMethod> originalMethodSignatures,
- Map<DexMethod, DexMethod> originalConstructorSignatures,
+ Map<DexMethod, DexMethod> extraOriginalMethodSignatures,
+ Map<DexField, DexField> extraOriginalFieldSignatures,
GraphLens previousLens) {
super(
mergedClasses.getForwardMap(),
@@ -48,7 +49,8 @@
appView.dexItemFactory());
this.appView = appView;
this.methodExtraParameters = methodExtraParameters;
- this.originalConstructorSignatures = originalConstructorSignatures;
+ this.extraOriginalFieldSignatures = extraOriginalFieldSignatures;
+ this.extraOriginalMethodSignatures = extraOriginalMethodSignatures;
this.mergedClasses = mergedClasses;
}
@@ -59,13 +61,22 @@
@Override
public DexMethod getOriginalMethodSignature(DexMethod method) {
- DexMethod originalConstructor = originalConstructorSignatures.get(method);
+ DexMethod originalConstructor = extraOriginalMethodSignatures.get(method);
if (originalConstructor == null) {
return super.getOriginalMethodSignature(method);
}
return getPrevious().getOriginalMethodSignature(originalConstructor);
}
+ @Override
+ public DexField getOriginalFieldSignature(DexField field) {
+ DexField originalField = extraOriginalFieldSignatures.get(field);
+ if (originalField == null) {
+ return super.getOriginalFieldSignature(field);
+ }
+ return getPrevious().getOriginalFieldSignature(originalField);
+ }
+
/**
* If an overloaded constructor is requested, add the constructor id as a parameter to the
* constructor. Otherwise return the lookup on the underlying graph lens.
@@ -86,10 +97,8 @@
}
public static class Builder {
- private final BiMap<DexField, DexField> fieldMap = HashBiMap.create();
-
+ private ManyToOneMap<DexField, DexField> fieldMap = new ManyToOneMap<>();
private ManyToOneMap<DexMethod, DexMethod> methodMap = new ManyToOneMap<>();
-
private final Map<DexMethod, List<ExtraParameter>> methodExtraParameters =
new IdentityHashMap<>();
@@ -104,17 +113,24 @@
assert false;
return group.iterator().next();
});
- BiMap<DexField, DexField> originalFieldSignatures = fieldMap.inverse();
+ ManyToOneInverseMap<DexField, DexField> inverseFieldMap =
+ fieldMap.inverse(
+ group -> {
+ // Every group should have a representative. Fail in debug mode.
+ assert false;
+ return group.iterator().next();
+ });
return new HorizontalClassMergerGraphLens(
appView,
mergedClasses,
methodExtraParameters,
- fieldMap,
+ fieldMap.getForwardMap(),
methodMap.getForwardMap(),
- originalFieldSignatures,
+ inverseFieldMap.getBiMap(),
inverseMethodMap.getBiMap(),
inverseMethodMap.getExtraMap(),
+ inverseFieldMap.getExtraMap(),
appView.graphLens());
}
@@ -122,12 +138,18 @@
methodMap = methodMap.remap(remapMethods, Function.identity(), Function.identity());
}
- public Builder mapField(DexField from, DexField to) {
- DexField previousFrom = fieldMap.inverse().remove(from);
- if (previousFrom != null) {
- from = previousFrom;
- }
+ public void remapFields(BiMap<DexField, DexField> remapFields) {
+ fieldMap = fieldMap.remap(remapFields, Function.identity(), Function.identity());
+ }
+
+ public Builder moveField(DexField from, DexField to) {
fieldMap.put(from, to);
+ fieldMap.putInverse(from, to);
+ return this;
+ }
+
+ public Builder setRepresentativeField(DexField from, DexField to) {
+ fieldMap.setRepresentative(from, to);
return this;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
index 080102a..5432ef1 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
@@ -13,6 +13,7 @@
import java.util.Collection;
import java.util.Map;
import java.util.Set;
+import java.util.function.BiConsumer;
public class HorizontallyMergedClasses implements MergedClasses {
@@ -26,6 +27,11 @@
return new HorizontallyMergedClasses(new BidirectionalManyToOneMap<>());
}
+ @Override
+ public void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer) {
+ mergedClasses.forEach(consumer);
+ }
+
public DexType getMergeTargetOrDefault(DexType type) {
return mergedClasses.getOrDefault(type, type);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java
index 1a3a965..92130de 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java
@@ -38,7 +38,16 @@
MultiClassPolicy policy, LinkedList<List<DexProgramClass>> groups) {
// For each group apply the multi class policy and add all the new groups together.
return groups.stream()
- .flatMap(group -> policy.apply(group).stream())
+ .flatMap(
+ group -> {
+ int previousNumberOfClasses = group.size();
+ Collection<List<DexProgramClass>> newGroups = policy.apply(group);
+ policy.numberOfRemovedClasses += previousNumberOfClasses;
+ for (List<DexProgramClass> newGroup : newGroups) {
+ policy.numberOfRemovedClasses -= newGroup.size();
+ }
+ return newGroups.stream();
+ })
.collect(Collectors.toCollection(LinkedList::new));
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
index a7ce343..49399bf 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -46,6 +46,7 @@
private final AppView<AppInfoWithLiveness> appView;
private final DexItemFactory dexItemFactory;
private final BiMap<DexMethod, DexMethod> movedMethods = HashBiMap.create();
+ private final BiMap<DexField, DexField> movedFields = HashBiMap.create();
private final SyntheticArgumentClass syntheticArgumentClass;
private final BiMap<DexMethodSignature, DexMethodSignature> reservedInterfaceSignatures =
HashBiMap.create();
@@ -129,6 +130,7 @@
}
lensBuilder.remapMethods(movedMethods);
+ lensBuilder.remapFields(movedFields);
HorizontalClassMergerGraphLens lens = lensBuilder.build(appView, mergedClasses);
fieldAccessChangesBuilder.build(this::fixupMethodReference).modify(appView);
@@ -381,7 +383,7 @@
}
if (newField != encodedField.field) {
- lensBuilder.mapField(field, newField);
+ movedFields.put(field, newField);
setter.setField(i, encodedField.toTypeSubstitutedField(newField));
}
}
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 07a3172..258b1c7 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
@@ -20,17 +21,20 @@
import com.android.tools.r8.ir.synthetic.AbstractSynthesizedCode;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.OptionalBool;
+import com.android.tools.r8.utils.structural.Ordered;
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.Collection;
+import java.util.List;
public class VirtualMethodMerger {
private final DexProgramClass target;
private final DexItemFactory dexItemFactory;
- private final Collection<ProgramMethod> methods;
+ private final List<ProgramMethod> methods;
private final DexField classIdField;
private final AppView<AppInfoWithLiveness> appView;
private final DexMethod superMethod;
@@ -38,7 +42,7 @@
public VirtualMethodMerger(
AppView<AppInfoWithLiveness> appView,
DexProgramClass target,
- Collection<ProgramMethod> methods,
+ List<ProgramMethod> methods,
DexField classIdField,
DexMethod superMethod) {
this.dexItemFactory = appView.dexItemFactory();
@@ -50,7 +54,7 @@
}
public static class Builder {
- private final Collection<ProgramMethod> methods = new ArrayList<>();
+ private final List<ProgramMethod> methods = new ArrayList<>();
public Builder add(ProgramMethod constructor) {
methods.add(constructor);
@@ -122,63 +126,130 @@
private MethodAccessFlags getAccessFlags() {
// TODO(b/164998929): ensure this behaviour is correct, should probably calculate upper bound
- MethodAccessFlags flags = methods.iterator().next().getDefinition().getAccessFlags().copy();
+ MethodAccessFlags flags = methods.iterator().next().getAccessFlags().copy();
+ if (flags.isAbstract()
+ && Iterables.any(methods, method -> !method.getAccessFlags().isAbstract())) {
+ flags.unsetAbstract();
+ }
if (flags.isFinal() && Iterables.any(methods, method -> !method.getAccessFlags().isFinal())) {
flags.unsetFinal();
}
return flags;
}
+ private DexMethod getNewMethodReference() {
+ return ListUtils.first(methods).getReference().withHolder(target, dexItemFactory);
+ }
+
+ /**
+ * If there is a super method and all methods are abstract, then we can simply remove all abstract
+ * methods.
+ */
+ private boolean isNop() {
+ return superMethod != null
+ && Iterables.all(methods, method -> method.getDefinition().isAbstract());
+ }
+
+ /**
+ * If the method is present on all classes in the merge group, and there is at most one
+ * non-abstract method, then we can simply move that method (or the first abstract method) to the
+ * target class.
+ */
+ private boolean isTrivial() {
+ if (superMethod != null) {
+ return false;
+ }
+ if (methods.size() == 1) {
+ return true;
+ }
+ int numberOfNonAbstractMethods =
+ Iterables.size(Iterables.filter(methods, method -> !method.getDefinition().isAbstract()));
+ return numberOfNonAbstractMethods <= 1;
+ }
+
/**
* If there is only a single method that does not override anything then it is safe to just move
* it to the target type if it is not already in it.
*/
- public void mergeTrivial(
+ private void mergeTrivial(
ClassMethodsBuilder classMethodsBuilder, HorizontalClassMergerGraphLens.Builder lensBuilder) {
- DexEncodedMethod method = methods.iterator().next().getDefinition();
+ DexMethod newMethodReference = getNewMethodReference();
- if (method.getHolderType() != target.type) {
- // If the method is not in the target type, move it and record it in the lens.
- DexMethod originalReference = method.getReference();
- method = method.toRenamedHolderMethod(target.type, dexItemFactory);
- lensBuilder.moveMethod(originalReference, method.getReference());
+ // Find the first non-abstract method. If all are abstract, then select the first method.
+ ProgramMethod representative =
+ Iterables.find(methods, method -> !method.getDefinition().isAbstract(), null);
+ if (representative == null) {
+ representative = ListUtils.first(methods);
}
- classMethodsBuilder.addVirtualMethod(method);
+ for (ProgramMethod method : methods) {
+ if (method.getReference() == representative.getReference()) {
+ lensBuilder.moveMethod(method.getReference(), newMethodReference);
+ } else {
+ lensBuilder.mapMethod(method.getReference(), newMethodReference);
+ }
+ }
+
+ if (representative.getHolderType() == target.getType()) {
+ classMethodsBuilder.addVirtualMethod(representative.getDefinition());
+ } else {
+ // If the method is not in the target type, move it.
+ OptionalBool isLibraryMethodOverride =
+ representative.getDefinition().isLibraryMethodOverride();
+ classMethodsBuilder.addVirtualMethod(
+ representative
+ .getDefinition()
+ .toTypeSubstitutedMethod(
+ newMethodReference,
+ builder -> builder.setIsLibraryMethodOverrideIfKnown(isLibraryMethodOverride)));
+ }
}
public void merge(
ClassMethodsBuilder classMethodsBuilder,
HorizontalClassMergerGraphLens.Builder lensBuilder,
FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
- Reference2IntMap classIdentifiers) {
-
+ Reference2IntMap<DexType> classIdentifiers) {
assert !methods.isEmpty();
+ // Handle nop merges.
+ if (isNop()) {
+ return;
+ }
+
// Handle trivial merges.
- if (superMethod == null && methods.size() == 1) {
+ if (isTrivial()) {
mergeTrivial(classMethodsBuilder, lensBuilder);
return;
}
+
Int2ReferenceSortedMap<DexMethod> classIdToMethodMap = new Int2ReferenceAVLTreeMap<>();
CfVersion classFileVersion = null;
+ ProgramMethod representative = null;
for (ProgramMethod method : methods) {
+ if (method.getDefinition().isAbstract()) {
+ continue;
+ }
if (method.getDefinition().hasClassFileVersion()) {
CfVersion methodVersion = method.getDefinition().getClassFileVersion();
- classFileVersion = CfVersion.maxAllowNull(classFileVersion, methodVersion);
+ classFileVersion = Ordered.maxIgnoreNull(classFileVersion, methodVersion);
}
DexMethod newMethod = moveMethod(classMethodsBuilder, method);
lensBuilder.mapMethod(newMethod, newMethod);
lensBuilder.mapMethodInverse(method.getReference(), newMethod);
classIdToMethodMap.put(classIdentifiers.getInt(method.getHolderType()), newMethod);
+ if (representative == null) {
+ representative = method;
+ }
}
+ assert representative != null;
+
// Use the first of the original methods as the original method for the merged constructor.
- DexMethod templateReference = methods.iterator().next().getReference();
DexMethod originalMethodReference =
- appView.graphLens().getOriginalMethodSignature(templateReference);
+ appView.graphLens().getOriginalMethodSignature(representative.getReference());
DexMethod bridgeMethodReference =
dexItemFactory.createFreshMethodName(
originalMethodReference.getName().toSourceString() + "$bridge",
@@ -187,8 +258,7 @@
originalMethodReference.getHolderType(),
classMethodsBuilder::isFresh);
- DexMethod newMethodReference =
- dexItemFactory.createMethod(target.type, templateReference.proto, templateReference.name);
+ DexMethod newMethodReference = getNewMethodReference();
AbstractSynthesizedCode synthesizedCode =
new VirtualMethodEntryPointSynthesizedCode(
classIdToMethodMap,
@@ -206,10 +276,17 @@
synthesizedCode,
true,
classFileVersion);
+ if (!representative.getDefinition().isLibraryMethodOverride().isUnknown()) {
+ newMethod.setLibraryMethodOverride(representative.getDefinition().isLibraryMethodOverride());
+ }
- // Map each old method to the newly synthesized method in the graph lens.
+ // Map each old non-abstract method to the newly synthesized method in the graph lens.
for (ProgramMethod oldMethod : methods) {
- lensBuilder.moveMethod(oldMethod.getReference(), newMethodReference);
+ if (oldMethod.getDefinition().isAbstract()) {
+ lensBuilder.mapMethod(oldMethod.getReference(), newMethodReference);
+ } else {
+ lensBuilder.moveMethod(oldMethod.getReference(), newMethodReference);
+ }
}
lensBuilder.recordExtraOriginalSignature(bridgeMethodReference, newMethodReference);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckAbstractClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckAbstractClasses.java
new file mode 100644
index 0000000..4839801
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckAbstractClasses.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.horizontalclassmerging.policies;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.Lists;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+public class CheckAbstractClasses extends MultiClassPolicy {
+
+ private final InternalOptions options;
+
+ public CheckAbstractClasses(AppView<AppInfoWithLiveness> appView) {
+ this.options = appView.options();
+ }
+
+ @Override
+ public Collection<List<DexProgramClass>> apply(List<DexProgramClass> group) {
+ if (options.canUseAbstractMethodOnNonAbstractClass()) {
+ // We can just make the target class non-abstract if one of the classes in the group
+ // is non-abstract.
+ return Lists.<List<DexProgramClass>>newArrayList(group);
+ }
+ List<DexProgramClass> abstractClasses = new LinkedList<>();
+ List<DexProgramClass> nonAbstractClasses = new LinkedList<>();
+ for (DexProgramClass clazz : group) {
+ if (clazz.isAbstract()) {
+ abstractClasses.add(clazz);
+ } else {
+ nonAbstractClasses.add(clazz);
+ }
+ }
+ List<List<DexProgramClass>> newGroups = new LinkedList<>();
+ if (abstractClasses.size() > 1) {
+ newGroups.add(abstractClasses);
+ }
+ if (nonAbstractClasses.size() > 1) {
+ newGroups.add(nonAbstractClasses);
+ }
+ return newGroups;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoAbstractClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoAbstractClasses.java
deleted file mode 100644
index fdc849d..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoAbstractClasses.java
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.horizontalclassmerging.policies;
-
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
-import com.google.common.collect.Iterables;
-
-public class NoAbstractClasses extends SingleClassPolicy {
- @Override
- public boolean canMerge(DexProgramClass program) {
- return !program.isAbstract()
- && !Iterables.any(program.virtualMethods(), DexEncodedMethod::isAbstract);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceFields.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceFields.java
deleted file mode 100644
index 326e94a..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceFields.java
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.horizontalclassmerging.policies;
-
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
-
-public class NoInstanceFields extends SingleClassPolicy {
- @Override
- public boolean canMerge(DexProgramClass program) {
- return !program.hasInstanceFields();
- }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinLambdas.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinLambdas.java
new file mode 100644
index 0000000..a137e88
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinLambdas.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.horizontalclassmerging.policies;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
+public class NoKotlinLambdas extends SingleClassPolicy {
+ private final AppView<AppInfoWithLiveness> appView;
+
+ public NoKotlinLambdas(AppView<AppInfoWithLiveness> appView) {
+ this.appView = appView;
+ }
+
+ @Override
+ public boolean shouldSkipPolicy() {
+ return appView.options().enableHorizontalClassMergingOfKotlinLambdas;
+ }
+
+ @Override
+ public boolean canMerge(DexProgramClass program) {
+ if (program.getKotlinInfo().isNoKotlinInformation()
+ || !program.getKotlinInfo().isSyntheticClass()) {
+ return true;
+ }
+
+ return !program.getKotlinInfo().asSyntheticClass().isLambda();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoOverlappingConstructors.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoOverlappingConstructors.java
index 474fcea..245b391 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoOverlappingConstructors.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoOverlappingConstructors.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
import java.util.ArrayList;
import java.util.Collection;
@@ -35,8 +34,7 @@
private Set<DexProgramClass> sortedClassSet(Collection<DexProgramClass> classes) {
Set<DexProgramClass> set =
- new TreeSet<DexProgramClass>(
- Comparator.comparing(DexProgramClass::getType, DexType::slowCompareTo));
+ new TreeSet<DexProgramClass>(Comparator.comparing(DexProgramClass::getType));
set.addAll(classes);
return set;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotMatchedByNoHorizontalClassMerging.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotMatchedByNoHorizontalClassMerging.java
index 5b11ff5..bfe0f2a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotMatchedByNoHorizontalClassMerging.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotMatchedByNoHorizontalClassMerging.java
@@ -15,19 +15,19 @@
public class NotMatchedByNoHorizontalClassMerging extends SingleClassPolicy {
+ private final AppView<AppInfoWithLiveness> appView;
private final Set<DexType> deadEnumLiteMaps;
- private final Set<DexType> neverMergeClassHorizontally;
public NotMatchedByNoHorizontalClassMerging(AppView<AppInfoWithLiveness> appView) {
- deadEnumLiteMaps =
+ this.appView = appView;
+ this.deadEnumLiteMaps =
appView.withProtoEnumShrinker(
EnumLiteProtoShrinker::getDeadEnumLiteMaps, Collections.emptySet());
- neverMergeClassHorizontally = appView.appInfo().getNoHorizontalClassMergingSet();
}
@Override
public boolean canMerge(DexProgramClass program) {
return !deadEnumLiteMaps.contains(program.getType())
- && !neverMergeClassHorizontally.contains(program.getType());
+ && !appView.appInfo().isNoHorizontalClassMergingOfType(program.getType());
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoMainDex.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoMainDex.java
index f8f0a3f..783ede5 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoMainDex.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoMainDex.java
@@ -6,48 +6,40 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
+import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
+import com.android.tools.r8.horizontalclassmerging.policies.PreventMergeIntoMainDex.MainDexClassification;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.MainDexClasses;
import com.android.tools.r8.shaking.MainDexTracingResult;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-public class PreventMergeIntoMainDex extends MultiClassPolicy {
+public class PreventMergeIntoMainDex extends MultiClassSameReferencePolicy<MainDexClassification> {
private final MainDexClasses mainDexClasses;
private final MainDexTracingResult mainDexTracingResult;
+ enum MainDexClassification {
+ MAIN_DEX_LIST,
+ MAIN_DEX_ROOT,
+ MAIN_DEX_DEPENDENCY,
+ NOT_IN_MAIN_DEX
+ }
+
public PreventMergeIntoMainDex(
AppView<AppInfoWithLiveness> appView, MainDexTracingResult mainDexTracingResult) {
this.mainDexClasses = appView.appInfo().getMainDexClasses();
this.mainDexTracingResult = mainDexTracingResult;
}
- public boolean isMainDexClass(DexProgramClass clazz) {
- return mainDexClasses.contains(clazz) || mainDexTracingResult.contains(clazz);
- }
-
@Override
- public Collection<List<DexProgramClass>> apply(List<DexProgramClass> group) {
- List<DexProgramClass> mainDexMembers = new LinkedList<>();
- Iterator<DexProgramClass> iterator = group.iterator();
- while (iterator.hasNext()) {
- DexProgramClass clazz = iterator.next();
- if (isMainDexClass(clazz)) {
- iterator.remove();
- mainDexMembers.add(clazz);
- }
+ public MainDexClassification getMergeKey(DexProgramClass clazz) {
+ if (mainDexClasses.contains(clazz)) {
+ return MainDexClassification.MAIN_DEX_LIST;
}
-
- Collection<List<DexProgramClass>> newGroups = new LinkedList<>();
- if (!isTrivial(mainDexMembers)) {
- newGroups.add(mainDexMembers);
+ if (mainDexTracingResult.isRoot(clazz)) {
+ return MainDexClassification.MAIN_DEX_ROOT;
}
- if (!isTrivial(group)) {
- newGroups.add(group);
+ if (mainDexTracingResult.isDependency(clazz)) {
+ return MainDexClassification.MAIN_DEX_DEPENDENCY;
}
- return newGroups;
+ return MainDexClassification.NOT_IN_MAIN_DEX;
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFields.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFields.java
new file mode 100644
index 0000000..63134de
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFields.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.horizontalclassmerging.policies;
+
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.FieldMultiset;
+import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
+
+public class SameFields extends MultiClassSameReferencePolicy<FieldMultiset> {
+
+ @Override
+ public FieldMultiset getMergeKey(DexProgramClass clazz) {
+ return new FieldMultiset(clazz);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
index 29d38c6..6e46731 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
@@ -328,12 +328,7 @@
}
if (state.hasTrackedValueEscaped()) {
DexType holder = staticPut.getField().holder;
- if (holder.classInitializationMayHaveSideEffects(
- appView,
- // Types that are a super type of the current context are guaranteed to be
- // initialized already.
- type -> appView.isSubtype(context.getHolderType(), type).isTrue(),
- Sets.newIdentityHashSet())) {
+ if (holder.classInitializationMayHaveSideEffectsInContext(appView, context)) {
return true;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
index edd1b91..eeeea70 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
@@ -134,7 +134,7 @@
Set<DexType> interfaces = getInterfaces();
if (interfaces != null) {
List<DexType> sortedInterfaces = new ArrayList<>(interfaces);
- sortedInterfaces.sort(DexType::slowCompareTo);
+ sortedInterfaces.sort(DexType::compareTo);
builder.append(
sortedInterfaces.stream().map(DexType::toString).collect(Collectors.joining(", ")));
}
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 17b49b5..3e0f741 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
@@ -38,6 +38,10 @@
this.type = type;
}
+ public static Builder builder() {
+ return new Builder();
+ }
+
@Override
public int opcode() {
return Opcodes.CHECK_CAST;
@@ -230,4 +234,30 @@
public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
return false;
}
+
+ public static class Builder extends BuilderBase<Builder, CheckCast> {
+
+ private DexType castType;
+ private Value object;
+
+ public Builder setCastType(DexType castType) {
+ this.castType = castType;
+ return this;
+ }
+
+ public Builder setObject(Value object) {
+ this.object = object;
+ return this;
+ }
+
+ @Override
+ public CheckCast build() {
+ return amend(new CheckCast(outValue, object, castType));
+ }
+
+ @Override
+ public Builder self() {
+ return this;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
index fde9019..7ec3198 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -20,7 +20,6 @@
import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.google.common.collect.Sets;
import java.util.Collections;
import java.util.List;
@@ -109,11 +108,7 @@
}
}
// May trigger <clinit> that may have side effects.
- if (field.holder.classInitializationMayHaveSideEffects(
- appView,
- // Types that are a super type of `context` are guaranteed to be initialized already.
- type -> appView.isSubtype(context.getHolderType(), type).isTrue(),
- Sets.newIdentityHashSet())) {
+ if (field.holder.classInitializationMayHaveSideEffectsInContext(appView, context)) {
return true;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InitClass.java b/src/main/java/com/android/tools/r8/ir/code/InitClass.java
index c878d9a..aceeab3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InitClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InitClass.java
@@ -22,7 +22,6 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.google.common.collect.Sets;
public class InitClass extends Instruction {
@@ -111,11 +110,7 @@
.isPossiblyFalse()) {
return true;
}
- if (clazz.classInitializationMayHaveSideEffects(
- appView,
- // Types that are a super type of `context` are guaranteed to be initialized already.
- type -> appView.isSubtype(context.getHolderType(), type).isTrue(),
- Sets.newIdentityHashSet())) {
+ if (clazz.classInitializationMayHaveSideEffectsInContext(appView, context)) {
return true;
}
return false;
@@ -132,11 +127,7 @@
if (appView.enableWholeProgramOptimizations()) {
// In R8, check if the class initialization of `clazz` or any of its ancestor types may have
// side effects.
- return clazz.classInitializationMayHaveSideEffects(
- appView,
- // Types that are a super type of `context` are guaranteed to be initialized already.
- type -> appView.isSubtype(context.getHolderType(), type).isTrue(),
- Sets.newIdentityHashSet());
+ return clazz.classInitializationMayHaveSideEffectsInContext(appView, context);
} else {
// In D8, this instruction may trigger class initialization if `clazz` is different from the
// current context.
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index 9bae360..3139d20 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -28,7 +28,6 @@
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Sets;
import java.util.Collections;
import java.util.List;
@@ -209,11 +208,11 @@
return true;
}
- DexEncodedMethod singleTarget = resolutionResult.getSingleTarget();
+ DexClassAndMethod singleTarget = resolutionResult.getResolutionPair();
assert singleTarget != null;
// Verify that the target method is static and accessible.
- if (!singleTarget.isStatic()
+ if (!singleTarget.getDefinition().isStatic()
|| resolutionResult.isAccessibleFrom(context, appInfoWithLiveness).isPossiblyFalse()) {
return true;
}
@@ -223,7 +222,7 @@
return false;
}
- if (singleTarget.getOptimizationInfo().mayHaveSideEffects()) {
+ if (singleTarget.getDefinition().getOptimizationInfo().mayHaveSideEffects()) {
return true;
}
@@ -232,13 +231,8 @@
}
return singleTarget
- .holder()
- .classInitializationMayHaveSideEffects(
- appView,
- // Types that are a super type of `context` are guaranteed to be initialized
- // already.
- type -> appInfoWithLiveness.isSubtype(context.getHolderType(), type),
- Sets.newIdentityHashSet());
+ .getHolder()
+ .classInitializationMayHaveSideEffectsInContext(appView, context);
}
public static class Builder extends BuilderBase<Builder, InvokeStatic> {
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index 704de30..39f2aee 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -26,7 +26,6 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.google.common.collect.Sets;
public class NewInstance extends Instruction {
@@ -176,11 +175,7 @@
}
// Verify that the new-instance instruction won't lead to class initialization.
- if (definition.classInitializationMayHaveSideEffects(
- appView,
- // Types that are a super type of `context` are guaranteed to be initialized already.
- type -> appViewWithLiveness.appInfo().isSubtype(context.getHolderType(), type),
- Sets.newIdentityHashSet())) {
+ if (definition.classInitializationMayHaveSideEffectsInContext(appViewWithLiveness, context)) {
return true;
}
@@ -213,11 +208,7 @@
if (appView.enableWholeProgramOptimizations()) {
// In R8, check if the class initialization of the holder or any of its ancestor types may
// have side effects.
- return clazz.classInitializationMayHaveSideEffects(
- appView,
- // Types that are a super type of `context` are guaranteed to be initialized already.
- type -> appView.isSubtype(context.getHolderType(), type).isTrue(),
- Sets.newIdentityHashSet());
+ return clazz.classInitializationMayHaveSideEffectsInContext(appView, context);
} else {
// In D8, this instruction may trigger class initialization if the holder of the field is
// different from the current context.
diff --git a/src/main/java/com/android/tools/r8/ir/code/Position.java b/src/main/java/com/android/tools/r8/ir/code/Position.java
index 6cc4f93..ab28d55 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Position.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Position.java
@@ -134,9 +134,9 @@
return 0;
}
return Comparator.comparingInt((Position p) -> p.line)
- .thenComparing(p -> p.file, Comparator.nullsFirst(DexString::slowCompareTo))
+ .thenComparing(p -> p.file, Comparator.nullsFirst(DexString::compareTo))
.thenComparing(p -> p.synthetic)
- .thenComparing(p -> p.method, DexMethod::slowCompareTo)
+ .thenComparing(p -> p.method)
.thenComparing(p -> p.callerPosition, Comparator.nullsFirst(Position::compareTo))
.compare(this, o);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index 8a3f867..70f0502 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -29,7 +29,6 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.google.common.collect.Sets;
import java.util.Set;
public class StaticGet extends FieldInstruction implements StaticFieldInstruction {
@@ -237,11 +236,7 @@
if (appView.enableWholeProgramOptimizations()) {
// In R8, check if the class initialization of the holder or any of its ancestor types may
// have side effects.
- return holder.classInitializationMayHaveSideEffects(
- appView,
- // Types that are a super type of `context` are guaranteed to be initialized already.
- type -> appView.isSubtype(context.getHolderType(), type).isTrue(),
- Sets.newIdentityHashSet());
+ return holder.classInitializationMayHaveSideEffectsInContext(appView, context);
} else {
// In D8, this instruction may trigger class initialization if the holder of the field is
// different from the current context.
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index 9df14cc..82d67b0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -29,7 +29,6 @@
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.ProguardMemberRule;
-import com.google.common.collect.Sets;
public class StaticPut extends FieldInstruction implements StaticFieldInstruction {
@@ -232,11 +231,7 @@
if (appView.enableWholeProgramOptimizations()) {
// In R8, check if the class initialization of the holder or any of its ancestor types may
// have side effects.
- return holder.classInitializationMayHaveSideEffects(
- appView,
- // Types that are a super type of `context` are guaranteed to be initialized already.
- type -> appView.isSubtype(context.getHolderType(), type).isTrue(),
- Sets.newIdentityHashSet());
+ return holder.classInitializationMayHaveSideEffectsInContext(appView, context);
} else {
// In D8, this instruction may trigger class initialization if the holder of the field is
// different from the current context.
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
index 2084df9..66d7fa4 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
@@ -203,9 +203,7 @@
@Override
public int compareTo(Node other) {
- return getProgramMethod()
- .getReference()
- .slowCompareTo(other.getProgramMethod().getReference());
+ return getProgramMethod().getReference().compareTo(other.getProgramMethod().getReference());
}
@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 8ba06e0..ec95001 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
@@ -1206,7 +1206,7 @@
assert !method.isProcessed() || !isDebugMode;
assert !method.isProcessed()
|| !appView.enableWholeProgramOptimizations()
- || !appView.appInfo().withLiveness().neverReprocess.contains(method.method);
+ || !appView.appInfo().withLiveness().isNeverReprocessMethod(method.method);
if (lambdaMerger != null) {
timing.begin("Merge lambdas");
@@ -1769,7 +1769,7 @@
DexString highestSortingReferencedString = method.getCode().asDexCode().highestSortingString;
if (highestSortingReferencedString != null) {
if (highestSortingString == null
- || highestSortingReferencedString.slowCompareTo(highestSortingString) > 0) {
+ || highestSortingReferencedString.compareTo(highestSortingString) > 0) {
highestSortingString = highestSortingReferencedString;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index e1c9063..20cc2c6 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -379,21 +379,36 @@
{
InstanceGet instanceGet = current.asInstanceGet();
DexField field = instanceGet.getField();
- DexField actualField = rewriteFieldReference(field, method, graphLens);
+ FieldLookupResult lookup = graphLens.lookupFieldResult(field);
+ DexField rewrittenField = rewriteFieldReference(lookup, method);
DexMethod replacementMethod =
- graphLens.lookupGetFieldForMethod(actualField, method.getReference());
+ graphLens.lookupGetFieldForMethod(rewrittenField, method.getReference());
+ Value newOutValue = null;
if (replacementMethod != null) {
- Value newOutValue = makeOutValue(current, code);
+ newOutValue = makeOutValue(instanceGet, code);
iterator.replaceCurrentInstruction(
- new InvokeStatic(replacementMethod, newOutValue, current.inValues()));
- if (newOutValue != null && newOutValue.getType() != current.getOutType()) {
- affectedPhis.addAll(current.outValue().uniquePhiUsers());
- }
- } else if (actualField != field) {
- Value newOutValue = makeOutValue(instanceGet, code);
+ new InvokeStatic(replacementMethod, newOutValue, instanceGet.inValues()));
+ } else if (rewrittenField != field) {
+ newOutValue = makeOutValue(instanceGet, code);
iterator.replaceCurrentInstruction(
- new InstanceGet(newOutValue, instanceGet.object(), actualField));
- if (newOutValue != null && newOutValue.getType() != current.getOutType()) {
+ new InstanceGet(newOutValue, instanceGet.object(), rewrittenField));
+ }
+ if (newOutValue != null) {
+ if (lookup.hasCastType() && newOutValue.hasNonDebugUsers()) {
+ TypeElement castType =
+ TypeElement.fromDexType(
+ lookup.getCastType(), newOutValue.getType().nullability(), appView);
+ CheckCast checkCast =
+ CheckCast.builder()
+ .setCastType(lookup.getCastType())
+ .setFreshOutValue(code, castType)
+ .setObject(newOutValue)
+ .setPosition(instanceGet)
+ .build();
+ iterator.add(checkCast);
+ newOutValue.replaceUsers(checkCast.outValue());
+ affectedPhis.addAll(checkCast.outValue().uniquePhiUsers());
+ } else if (newOutValue.getType() != instanceGet.getOutType()) {
affectedPhis.addAll(newOutValue.uniquePhiUsers());
}
}
@@ -404,19 +419,20 @@
{
InstancePut instancePut = current.asInstancePut();
DexField field = instancePut.getField();
- DexField actualField = rewriteFieldReference(field, method, graphLens);
+ FieldLookupResult lookup = graphLens.lookupFieldResult(field);
+ DexField rewrittenField = rewriteFieldReference(lookup, method);
DexMethod replacementMethod =
- graphLens.lookupPutFieldForMethod(actualField, method.getReference());
+ graphLens.lookupPutFieldForMethod(rewrittenField, method.getReference());
if (replacementMethod != null) {
iterator.replaceCurrentInstruction(
- new InvokeStatic(replacementMethod, null, current.inValues()));
- } else if (actualField != field) {
+ new InvokeStatic(replacementMethod, null, instancePut.inValues()));
+ } else if (rewrittenField != field) {
Value rewrittenValue =
rewriteValueIfDefault(
- code, iterator, field.type, actualField.type, instancePut.value());
+ code, iterator, field.type, rewrittenField.type, instancePut.value());
InstancePut newInstancePut =
InstancePut.createPotentiallyInvalid(
- actualField, instancePut.object(), rewrittenValue);
+ rewrittenField, instancePut.object(), rewrittenValue);
iterator.replaceCurrentInstruction(newInstancePut);
}
}
@@ -426,20 +442,35 @@
{
StaticGet staticGet = current.asStaticGet();
DexField field = staticGet.getField();
- DexField actualField = rewriteFieldReference(field, method, graphLens);
+ FieldLookupResult lookup = graphLens.lookupFieldResult(field);
+ DexField rewrittenField = rewriteFieldReference(lookup, method);
DexMethod replacementMethod =
- graphLens.lookupGetFieldForMethod(actualField, method.getReference());
+ graphLens.lookupGetFieldForMethod(rewrittenField, method.getReference());
+ Value newOutValue = null;
if (replacementMethod != null) {
- Value newOutValue = makeOutValue(current, code);
+ newOutValue = makeOutValue(staticGet, code);
iterator.replaceCurrentInstruction(
- new InvokeStatic(replacementMethod, newOutValue, current.inValues()));
- if (newOutValue != null && newOutValue.getType() != current.getOutType()) {
- affectedPhis.addAll(newOutValue.uniquePhiUsers());
- }
- } else if (actualField != field) {
- Value newOutValue = makeOutValue(staticGet, code);
- iterator.replaceCurrentInstruction(new StaticGet(newOutValue, actualField));
- if (newOutValue != null && newOutValue.getType() != current.getOutType()) {
+ new InvokeStatic(replacementMethod, newOutValue, staticGet.inValues()));
+ } else if (rewrittenField != field) {
+ newOutValue = makeOutValue(staticGet, code);
+ iterator.replaceCurrentInstruction(new StaticGet(newOutValue, rewrittenField));
+ }
+ if (newOutValue != null) {
+ if (lookup.hasCastType() && newOutValue.hasNonDebugUsers()) {
+ TypeElement castType =
+ TypeElement.fromDexType(
+ lookup.getCastType(), newOutValue.getType().nullability(), appView);
+ CheckCast checkCast =
+ CheckCast.builder()
+ .setCastType(lookup.getCastType())
+ .setFreshOutValue(code, castType)
+ .setObject(newOutValue)
+ .setPosition(staticGet)
+ .build();
+ iterator.add(checkCast);
+ newOutValue.replaceUsers(checkCast.outValue());
+ affectedPhis.addAll(checkCast.outValue().uniquePhiUsers());
+ } else if (newOutValue.getType() != staticGet.getOutType()) {
affectedPhis.addAll(newOutValue.uniquePhiUsers());
}
}
@@ -450,18 +481,19 @@
{
StaticPut staticPut = current.asStaticPut();
DexField field = staticPut.getField();
- DexField actualField = rewriteFieldReference(field, method, graphLens);
+ FieldLookupResult lookup = graphLens.lookupFieldResult(field);
+ DexField actualField = rewriteFieldReference(lookup, method);
DexMethod replacementMethod =
graphLens.lookupPutFieldForMethod(actualField, method.getReference());
if (replacementMethod != null) {
iterator.replaceCurrentInstruction(
- new InvokeStatic(replacementMethod, current.outValue(), current.inValues()));
+ new InvokeStatic(
+ replacementMethod, staticPut.outValue(), staticPut.inValues()));
} else if (actualField != field) {
Value rewrittenValue =
rewriteValueIfDefault(
code, iterator, field.type, actualField.type, staticPut.value());
- StaticPut newStaticPut = new StaticPut(rewrittenValue, actualField);
- iterator.replaceCurrentInstruction(newStaticPut);
+ iterator.replaceCurrentInstruction(new StaticPut(rewrittenValue, actualField));
}
}
break;
@@ -586,16 +618,15 @@
assert code.hasNoVerticallyMergedClasses(appView);
}
- private DexField rewriteFieldReference(
- DexField reference, ProgramMethod context, GraphLens graphLens) {
- FieldLookupResult lookup = graphLens.lookupFieldResult(reference);
+ private DexField rewriteFieldReference(FieldLookupResult lookup, ProgramMethod context) {
if (lookup.hasReboundReference()) {
DexClass holder = appView.definitionFor(lookup.getReboundReference().getHolderType());
DexEncodedField definition = lookup.getReboundReference().lookupOnClass(holder);
if (definition != null) {
DexClassAndField field = DexClassAndField.create(holder, definition);
if (AccessControl.isMemberAccessible(field, holder, context, appView).isTrue()) {
- return MemberRebindingAnalysis.validMemberRebindingTargetFor(appView, field, reference);
+ return MemberRebindingAnalysis.validMemberRebindingTargetFor(
+ appView, field, lookup.getReference());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
index 4b44ada..ae5e3f9 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
@@ -33,20 +33,28 @@
public class LensCodeRewriterUtils {
+ private final AppView<?> appView;
private final DexDefinitionSupplier definitions;
private final GraphLens graphLens;
private final Map<DexProto, DexProto> protoFixupCache = new ConcurrentHashMap<>();
public LensCodeRewriterUtils(AppView<?> appView) {
- this(appView, appView.graphLens());
+ this.appView = appView;
+ this.definitions = appView;
+ this.graphLens = null;
}
public LensCodeRewriterUtils(DexDefinitionSupplier definitions, GraphLens graphLens) {
+ this.appView = null;
this.definitions = definitions;
this.graphLens = graphLens;
}
+ private GraphLens graphLens() {
+ return appView != null ? appView.graphLens() : graphLens;
+ }
+
public DexCallSite rewriteCallSite(DexCallSite callSite, ProgramMethod context) {
DexItemFactory dexItemFactory = definitions.dexItemFactory();
DexProto newMethodProto = rewriteProto(callSite.methodProto);
@@ -74,7 +82,7 @@
DexMethod invokedMethod = methodHandle.asMethod();
MethodHandleType oldType = methodHandle.type;
MethodLookupResult lensLookup =
- graphLens.lookupMethod(invokedMethod, context.getReference(), oldType.toInvokeType());
+ graphLens().lookupMethod(invokedMethod, context.getReference(), oldType.toInvokeType());
DexMethod rewrittenTarget = lensLookup.getReference();
DexMethod actualTarget;
MethodHandleType newType;
@@ -115,7 +123,7 @@
}
} else {
DexField field = methodHandle.asField();
- DexField actualField = graphLens.lookupField(field);
+ DexField actualField = graphLens().lookupField(field);
if (actualField != field) {
return new DexMethodHandle(methodHandle.type, actualField, methodHandle.isInterface);
}
@@ -158,7 +166,7 @@
return rewriteDexMethodType(value.asDexValueMethodType());
case TYPE:
DexType oldType = value.asDexValueType().value;
- DexType newType = graphLens.lookupType(oldType);
+ DexType newType = graphLens().lookupType(oldType);
return newType != oldType ? new DexValueType(newType) : value;
default:
return value;
@@ -168,7 +176,7 @@
public DexProto rewriteProto(DexProto proto) {
return definitions
.dexItemFactory()
- .applyClassMappingToProto(proto, graphLens::lookupType, protoFixupCache);
+ .applyClassMappingToProto(proto, graphLens()::lookupType, protoFixupCache);
}
private DexValueMethodHandle rewriteDexValueMethodHandle(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
index 48aaf3a..3d86936 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
@@ -8,6 +8,7 @@
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;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMethod;
@@ -28,6 +29,7 @@
import java.util.IdentityHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -122,20 +124,17 @@
PostMethodProcessor build(
AppView<AppInfoWithLiveness> appView, ExecutorService executorService, Timing timing)
throws ExecutionException {
- if (!appView.appInfo().reprocess.isEmpty()) {
+ Set<DexMethod> reprocessMethods = appView.appInfo().getReprocessMethods();
+ if (!reprocessMethods.isEmpty()) {
ProgramMethodSet set = ProgramMethodSet.create();
- appView
- .appInfo()
- .reprocess
- .forEach(
- reference -> {
- DexProgramClass clazz =
- asProgramClassOrNull(appView.definitionForHolder(reference));
- DexEncodedMethod definition = reference.lookupOnClass(clazz);
- if (definition != null) {
- set.createAndAdd(clazz, definition);
- }
- });
+ reprocessMethods.forEach(
+ reference -> {
+ DexProgramClass clazz = asProgramClassOrNull(appView.definitionForHolder(reference));
+ DexEncodedMethod definition = reference.lookupOnClass(clazz);
+ if (definition != null) {
+ set.createAndAdd(clazz, definition);
+ }
+ });
put(set);
}
if (methodsToReprocess.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index 758830d..0884ab5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -423,12 +423,32 @@
}
// We need to introduce them in deterministic order for deterministic compilation.
ArrayList<DexType> sortedEmulatedInterfaces = new ArrayList<>(emulatedInterfaces);
- Collections.sort(sortedEmulatedInterfaces, DexType::slowCompareTo);
+ Collections.sort(sortedEmulatedInterfaces);
List<GenericSignature.ClassTypeSignature> extraInterfaceSignatures = new ArrayList<>();
for (DexType extraInterface : sortedEmulatedInterfaces) {
extraInterfaceSignatures.add(
new GenericSignature.ClassTypeSignature(rewriter.getEmulatedInterface(extraInterface)));
}
+ // The emulated interface might already be implemented if the input class has gone through
+ // library desugaring already.
+ clazz
+ .getInterfaces()
+ .forEach(
+ iface -> {
+ for (int i = 0; i < extraInterfaceSignatures.size(); i++) {
+ if (extraInterfaceSignatures.get(i).type() == iface) {
+ if (!appView.options().desugarSpecificOptions().allowDesugaredInput) {
+ throw new CompilationError(
+ "Code has already been library desugared. Interface "
+ + iface.getDescriptor()
+ + " is already implemented by "
+ + clazz.getType().getDescriptor());
+ }
+ extraInterfaceSignatures.remove(i);
+ break;
+ }
+ }
+ });
clazz.asProgramClass().addExtraInterfaces(extraInterfaceSignatures);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
index 904ca7e..214c17a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
@@ -180,7 +180,7 @@
.computeIfAbsent(
newClass,
ignore ->
- new TreeSet<>((x, y) -> x.getReference().slowCompareTo(y.getReference())))
+ new TreeSet<>((x, y) -> x.getReference().compareTo(y.getReference())))
.add(
new DexEncodedMethod(
retargetMethod,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index d6f6264..ddcb3c7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -588,7 +588,7 @@
Map<DexType, List<DexType>> emulatedInterfacesHierarchy = new IdentityHashMap<>();
Set<DexType> processed = Sets.newIdentityHashSet();
ArrayList<DexType> emulatedInterfacesSorted = new ArrayList<>(emulatedInterfaces.keySet());
- emulatedInterfacesSorted.sort(DexType::slowCompareTo);
+ emulatedInterfacesSorted.sort(DexType::compareTo);
for (DexType interfaceType : emulatedInterfacesSorted) {
processEmulatedInterfaceHierarchy(interfaceType, processed, emulatedInterfacesHierarchy);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index fbeb14a..02388be 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -33,7 +33,7 @@
import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.DexValue.DexValueNull;
+import com.android.tools.r8.graph.DexValue.DexValueInt;
import com.android.tools.r8.graph.FieldAccessFlags;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
@@ -179,13 +179,13 @@
dexItemFactory.createField(iface.getType(), dexItemFactory.intType, "$desugar$clinit");
DexField clinitFieldReference =
dexItemFactory.createFreshFieldName(
- clinitFieldTemplateReference, candidate -> iface.lookupField(candidate) != null);
+ clinitFieldTemplateReference, candidate -> iface.lookupField(candidate) == null);
return new DexEncodedField(
clinitFieldReference,
FieldAccessFlags.builder().setPackagePrivate().setStatic().setSynthetic().build(),
FieldTypeSignature.noSignature(),
DexAnnotationSet.empty(),
- DexValueNull.NULL);
+ DexValueInt.DEFAULT);
}
private DexEncodedMethod createCompanionClassInitializer(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 11cff1a..9688ae8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -111,7 +111,7 @@
throws ExecutionException {
SortedProgramMethodSet nonDexAccessibilityBridges = SortedProgramMethodSet.create();
List<LambdaClass> sortedLambdaClasses = new ArrayList<>(lambdaClasses);
- sortedLambdaClasses.sort((x, y) -> x.type.slowCompareTo(y.type));
+ sortedLambdaClasses.sort((x, y) -> x.type.compareTo(y.type));
for (LambdaClass lambdaClass : sortedLambdaClasses) {
// This call may cause originalMethodSignatures to be updated.
ProgramMethod accessibilityBridge = lambdaClass.target.ensureAccessibilityIfNeeded(true);
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 0fc9b80..8a8d2d4 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
@@ -380,7 +380,7 @@
//
// For simplicity, we are conservative and consider all interfaces, not only the ones with
// default methods.
- if (!target.getHolder().classInitializationMayHaveSideEffects(appView)) {
+ if (!target.getHolder().classInitializationMayHaveSideEffectsInContext(appView, context)) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 945d9fe..3abbf9b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -707,7 +707,10 @@
assert !monitorEnterBlock.hasCatchHandlers();
InstructionListIterator monitorEnterBlockIterator = monitorEnterBlock.listIterator(code);
- monitorEnterBlockIterator.setInsertionPosition(Position.syntheticNone());
+ // MonitorEnter will only throw an NPE if the lock is null and that can only happen if the
+ // receiver was null. To preserve NPE's at call-sites for synchronized methods we therefore
+ // put in the invoke-position.
+ monitorEnterBlockIterator.setInsertionPosition(invoke.getPosition());
// If this is a static method, then the class object will act as the lock, so we load it
// using a const-class instruction.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
index ff7668a..1941743 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.VerticalClassMerger.SingleTypeMapperGraphLens;
import com.android.tools.r8.utils.TriFunction;
// Computes the inlining constraint for a given instruction.
@@ -62,7 +63,7 @@
}
private boolean isVerticalClassMerging() {
- return !graphLens.isIdentityLens();
+ return graphLens instanceof SingleTypeMapperGraphLens;
}
public ConstraintWithTarget forAlwaysMaterializingUser() {
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 202caa1..91f7642 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
@@ -456,14 +456,7 @@
appView, context, Instruction.SideEffectAssumption.CLASS_ALREADY_INITIALIZED)) {
return;
}
- boolean classInitializationMayHaveSideEffects =
- holder.classInitializationMayHaveSideEffects(
- appView,
- // Types that are a super type of `context` are guaranteed to be initialized
- // already.
- type -> appView.appInfo().isSubtype(context.getHolderType(), type),
- Sets.newIdentityHashSet());
- if (!classInitializationMayHaveSideEffects) {
+ if (!holder.classInitializationMayHaveSideEffectsInContext(appView, context)) {
iterator.removeOrReplaceByDebugLocalRead();
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 334238d..508e0f4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -337,7 +337,7 @@
return super.compareTo(other);
}
NewInstanceOutlineInstruction o = (NewInstanceOutlineInstruction) other;
- return clazz.slowCompareTo(o.clazz);
+ return clazz.compareTo(o.clazz);
}
@Override
@@ -435,7 +435,7 @@
return super.compareTo(other);
}
InvokeOutlineInstruction o = (InvokeOutlineInstruction) other;
- int result = method.slowCompareTo(o.method);
+ int result = method.compareTo(o.method);
if (result != 0) {
return result;
}
@@ -448,7 +448,7 @@
return result;
}
if (proto != null) {
- result = proto.slowCompareTo(o.proto);
+ result = proto.compareTo(o.proto);
if (result != 0) {
return result;
}
@@ -630,7 +630,7 @@
}
// First compare the proto.
int result;
- result = buildProto().slowCompareTo(other.buildProto());
+ result = buildProto().compareTo(other.buildProto());
if (result != 0) {
assert !equals(other);
return result;
@@ -1262,8 +1262,9 @@
private boolean removeMethodFromOutlineList(Outline outline) {
synchronized (outlineSites) {
assert ListUtils.removeFirstMatch(
- outlineSites.get(outline),
- element -> element.getDefinition() == method.getDefinition());
+ outlineSites.get(outline),
+ element -> element.getDefinition() == method.getDefinition())
+ .isPresent();
}
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
index b2ab54f..868bcc5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
+import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.value.SingleValue;
import com.android.tools.r8.ir.code.BasicBlock;
@@ -282,12 +283,7 @@
killAllNonFinalActiveFields();
} else if (instruction.isNewInstance()) {
NewInstance newInstance = instruction.asNewInstance();
- if (newInstance.clazz.classInitializationMayHaveSideEffects(
- appView,
- // Types that are a super type of `context` are guaranteed to be initialized
- // already.
- type -> appView.isSubtype(method.getHolderType(), type).isTrue(),
- Sets.newIdentityHashSet())) {
+ if (newInstance.clazz.classInitializationMayHaveSideEffectsInContext(appView, method)) {
killAllNonFinalActiveFields();
}
} else {
@@ -345,8 +341,11 @@
private boolean verifyWasInstanceInitializer() {
VerticallyMergedClasses verticallyMergedClasses = appView.verticallyMergedClasses();
+ HorizontallyMergedClasses horizontallyMergedClasses = appView.horizontallyMergedClasses();
assert verticallyMergedClasses != null;
- assert verticallyMergedClasses.isTarget(method.getHolderType());
+ assert horizontallyMergedClasses != null;
+ assert verticallyMergedClasses.isTarget(method.getHolderType())
+ || horizontallyMergedClasses.isMergeTarget(method.getHolderType());
assert appView
.dexItemFactory()
.isConstructor(appView.graphLens().getOriginalMethodSignature(method.getReference()));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index 96e5e9c..c11c8bf 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -305,7 +305,7 @@
private RewrittenPrototypeDescription getPrototypeChanges(
DexEncodedMethod encodedMethod, Strategy strategy) {
if (ArgumentRemovalUtils.isPinned(encodedMethod, appView)
- || appView.appInfo().keepConstantArguments.contains(encodedMethod.method)) {
+ || appView.appInfo().isKeepConstantArgumentsMethod(encodedMethod.method)) {
return RewrittenPrototypeDescription.none();
}
return RewrittenPrototypeDescription.createForUninstantiatedTypes(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
index 509daa1..6270034 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
@@ -284,7 +284,7 @@
private ArgumentInfoCollection collectUnusedArguments(
DexEncodedMethod method, MemberPool<DexMethod> methodPool) {
if (ArgumentRemovalUtils.isPinned(method, appView)
- || appView.appInfo().keepUnusedArguments.contains(method.method)) {
+ || appView.appInfo().isKeepUnusedArgumentsMethod(method.method)) {
return null;
}
// Only process classfile code objects.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index dab89c6..5d79d0e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -303,7 +303,7 @@
}
// Check for static initializers in this class or any of interfaces it implements.
- if (clazz.initializationOfParentTypesMayHaveSideEffects(appView)) {
+ if (clazz.classInitializationMayHaveSideEffects(appView)) {
return EligibilityStatus.NOT_ELIGIBLE;
}
return EligibilityStatus.ELIGIBLE;
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 6b0b61d..550a0e1 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
@@ -18,6 +18,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
import com.android.tools.r8.graph.LibraryMethod;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult;
@@ -165,11 +166,10 @@
if (eligibleClass == null) {
return EligibilityStatus.NOT_ELIGIBLE;
}
- if (eligibleClass.classInitializationMayHaveSideEffects(
- appView,
- // Types that are a super type of the current context are guaranteed to be initialized.
- type -> appView.isSubtype(method.getHolderType(), type).isTrue(),
- Sets.newIdentityHashSet())) {
+ if (method.getHolder() == eligibleClass) {
+ return EligibilityStatus.NOT_ELIGIBLE;
+ }
+ if (eligibleClass.classInitializationMayHaveSideEffectsInContext(appView, method)) {
return EligibilityStatus.NOT_ELIGIBLE;
}
return EligibilityStatus.ELIGIBLE;
@@ -178,10 +178,18 @@
assert root.isStaticGet();
StaticGet staticGet = root.asStaticGet();
+ SuccessfulFieldResolutionResult fieldResolutionResult =
+ appView.appInfo().resolveField(staticGet.getField()).asSuccessfulResolution();
+ if (fieldResolutionResult == null) {
+ return EligibilityStatus.NOT_ELIGIBLE;
+ }
+ if (method.getHolder() == fieldResolutionResult.getResolvedHolder()) {
+ return EligibilityStatus.NOT_ELIGIBLE;
+ }
if (staticGet.instructionMayHaveSideEffects(appView, method)) {
return EligibilityStatus.NOT_ELIGIBLE;
}
- DexEncodedField field = appView.appInfo().resolveField(staticGet.getField()).getResolvedField();
+ DexEncodedField field = fieldResolutionResult.getResolvedField();
FieldOptimizationInfo optimizationInfo = field.getOptimizationInfo();
ClassTypeElement dynamicLowerBoundType = optimizationInfo.getDynamicLowerBoundType();
if (dynamicLowerBoundType == null
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 27c125a..bfb520d 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
@@ -584,7 +584,7 @@
}
// We make the order deterministic.
for (List<T> value : encodedMembersMap.values()) {
- value.sort((m1, m2) -> m1.getReference().slowCompareTo(m2.getReference()));
+ value.sort((m1, m2) -> m1.getReference().compareTo(m2.getReference()));
}
return encodedMembersMap;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
index ebe815d..f1c2bdd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
@@ -13,10 +13,10 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfo;
import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -38,6 +38,7 @@
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.SwitchMapCollector;
+import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ArrayUtils;
import com.google.common.collect.Sets;
@@ -100,7 +101,8 @@
continue;
}
- AbstractValue abstractValue = definition.getOptimizationInfo().getAbstractValue();
+ FieldOptimizationInfo optimizationInfo = definition.getOptimizationInfo();
+ AbstractValue abstractValue = optimizationInfo.getAbstractValue();
if (!abstractValue.isSingleFieldValue()) {
continue;
}
@@ -158,15 +160,18 @@
continue;
}
- EnumValueInfo valueInfo = appView.appInfo().withLiveness().getEnumValueInfo(field);
- if (valueInfo == null) {
+ // Since the value is a single field value, the type should be exact.
+ assert abstractValue.isSingleFieldValue();
+ ClassTypeElement enumFieldType = optimizationInfo.getExactClassType(appView);
+ if (enumFieldType == null) {
+ assert false : "Expected to have an exact dynamic type for enum instance";
continue;
}
DexEncodedMethod singleTarget =
appView
.appInfo()
- .resolveMethodOnClass(factory.objectMembers.toString, valueInfo.type)
+ .resolveMethodOnClass(factory.objectMembers.toString, enumFieldType.getClassType())
.getSingleTarget();
if (singleTarget != null && singleTarget.method != factory.enumMembers.toString) {
continue;
@@ -180,22 +185,6 @@
nameValue.getDexString(),
ThrowingInfo.defaultForConstString(appView.options())));
newValue.addAffectedValuesTo(affectedValues);
- } else if (current.isArrayLength()) {
- // Rewrites MyEnum.values().length to a constant int.
- Instruction arrayDefinition = current.asArrayLength().array().getAliasedValue().definition;
- if (arrayDefinition != null && arrayDefinition.isInvokeStatic()) {
- DexMethod invokedMethod = arrayDefinition.asInvokeStatic().getInvokedMethod();
- DexProgramClass enumClass = appView.definitionForProgramType(invokedMethod.holder);
- if (enumClass != null
- && enumClass.isEnum()
- && factory.enumMembers.isValuesMethod(invokedMethod, enumClass)) {
- EnumValueInfoMap enumValueInfoMap =
- appView.appInfo().withLiveness().getEnumValueInfoMap(invokedMethod.holder);
- if (enumValueInfoMap != null) {
- iterator.replaceCurrentInstructionWithConstInt(code, enumValueInfoMap.size());
- }
- }
- }
}
}
if (!affectedValues.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java
index c203772..a0ea10c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java
@@ -143,7 +143,7 @@
for (DexProgramClass context : contexts) {
if (deterministicContext == null) {
deterministicContext = context.type;
- } else if (context.type.slowCompareTo(deterministicContext) < 0) {
+ } else if (context.type.compareTo(deterministicContext) < 0) {
deterministicContext = context.type;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/FieldOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/FieldOptimizationInfo.java
index 0365122..e70a91f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/FieldOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/FieldOptimizationInfo.java
@@ -4,9 +4,13 @@
package com.android.tools.r8.ir.optimize.info;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
public abstract class FieldOptimizationInfo {
@@ -26,6 +30,24 @@
public abstract TypeElement getDynamicUpperBoundType();
+ public ClassTypeElement getExactClassType(AppView<AppInfoWithLiveness> appView) {
+ ClassTypeElement dynamicLowerBoundType = getDynamicLowerBoundType();
+ TypeElement dynamicUpperBoundType = getDynamicUpperBoundType();
+ if (dynamicUpperBoundType == null || !dynamicUpperBoundType.isClassType()) {
+ return null;
+ }
+ DexType upperType = dynamicUpperBoundType.asClassType().getClassType();
+ if (dynamicLowerBoundType != null && upperType == dynamicLowerBoundType.getClassType()) {
+ return dynamicLowerBoundType;
+ }
+ DexClass upperClass = appView.definitionFor(upperType);
+ if (upperClass != null && upperClass.isEffectivelyFinal(appView)) {
+ assert dynamicLowerBoundType == null;
+ return ClassTypeElement.create(upperType, dynamicUpperBoundType.nullability(), appView);
+ }
+ return null;
+ }
+
public final TypeElement getDynamicUpperBoundTypeOrElse(TypeElement orElse) {
TypeElement dynamicUpperBoundType = getDynamicUpperBoundType();
return dynamicUpperBoundType != null ? dynamicUpperBoundType : orElse;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
index 775d7e9..9fa412a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
@@ -43,8 +43,7 @@
public static class Builder {
- TreeMap<DexField, InstanceFieldInitializationInfo> infos =
- new TreeMap<>(DexField::slowCompareTo);
+ TreeMap<DexField, InstanceFieldInitializationInfo> infos = new TreeMap<>(DexField::compareTo);
public void recordInitializationInfo(
DexEncodedField field, InstanceFieldInitializationInfo info) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
index b3e72bf..81c6a35 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
@@ -19,7 +19,7 @@
public static WhyAreYouNotInliningReporter createFor(
ProgramMethod callee, AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
- if (appView.appInfo().whyAreYouNotInlining.contains(callee.getReference())) {
+ if (appView.appInfo().isWhyAreYouNotInliningMethod(callee.getReference())) {
return new WhyAreYouNotInliningReporterImpl(
callee, context, appView.options().testing.whyAreYouNotInliningConsumer);
}
@@ -28,7 +28,7 @@
public static void handleInvokeWithUnknownTarget(
InvokeMethod invoke, AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
- if (appView.appInfo().whyAreYouNotInlining.isEmpty()) {
+ if (appView.appInfo().hasNoWhyAreYouNotInliningMethods()) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index 8aacbde..89ffafd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -62,6 +62,7 @@
import com.google.common.collect.Sets;
import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.Deque;
import java.util.IdentityHashMap;
import java.util.Iterator;
@@ -257,7 +258,7 @@
appView.testing().kotlinLambdaMergerFactoryForClass.apply(cls) != null
&& KotlinLambdaGroupIdFactory.hasValidAnnotations(kotlin, cls)
&& !appView.appInfo().getClassToFeatureSplitMap().isInFeature(cls))
- .sorted((a, b) -> a.type.slowCompareTo(b.type)) // Ensure stable ordering.
+ .sorted(Comparator.comparing(DexClass::getType)) // Ensure stable ordering.
.forEachOrdered(
lambda -> {
try {
@@ -268,14 +269,7 @@
group.add(lambda);
lambdas.put(lambda.type, group);
} catch (LambdaStructureError error) {
- if (error.reportable) {
- reporter.info(
- new StringDiagnostic(
- "Unrecognized Kotlin lambda ["
- + lambda.type.toSourceString()
- + "]: "
- + error.getMessage()));
- }
+ // Intentionally empty.
}
});
@@ -395,8 +389,7 @@
rewriteLambdaReferences(converter, executorService, appliedGraphLens);
this.mode = null;
- appView.setHorizontallyMergedLambdaClasses(
- new HorizontallyMergedLambdaClasses(lambdas.keySet()));
+ appView.setHorizontallyMergedLambdaClasses(new HorizontallyMergedLambdaClasses(lambdas));
}
private void analyzeLambdaClassesStructure(ExecutorService service) throws ExecutionException {
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 0179328..97ef2dc 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -46,6 +46,7 @@
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.PredicateUtils;
+import com.android.tools.r8.utils.structural.Ordered;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.Sets;
@@ -173,7 +174,7 @@
String sourceDebug = getSourceDebugExtension(clazz.annotations());
writer.visitSource(clazz.sourceFile != null ? clazz.sourceFile.toString() : null, sourceDebug);
CfVersion version = getClassFileVersion(clazz);
- if (version.isGreaterThanOrEqual(CfVersion.V1_8)) {
+ if (version.isGreaterThanOrEqualTo(CfVersion.V1_8)) {
// JDK8 and after ignore ACC_SUPER so unset it.
clazz.accessFlags.unsetSuper();
} else {
@@ -182,7 +183,10 @@
clazz.accessFlags.setSuper();
}
}
- int access = clazz.accessFlags.getAsCfAccessFlags();
+ int access =
+ options.testing.allowInvalidCfAccessFlags
+ ? clazz.accessFlags.materialize()
+ : clazz.accessFlags.getAsCfAccessFlags();
if (clazz.isDeprecated()) {
access = AsmUtils.withDeprecated(access);
}
@@ -227,8 +231,7 @@
}
if (options.desugarSpecificOptions().sortMethodsOnCfOutput) {
SortedSet<ProgramMethod> programMethodSortedSet =
- Sets.newTreeSet(
- (a, b) -> a.getDefinition().method.slowCompareTo(b.getDefinition().method));
+ Sets.newTreeSet((a, b) -> a.getDefinition().method.compareTo(b.getDefinition().method));
clazz.forEachProgramMethod(programMethodSortedSet::add);
programMethodSortedSet.forEach(
method -> writeMethod(method, version, rewriter, writer, defaults));
@@ -257,8 +260,9 @@
// In this case bridges have been introduced for the Cf back-end,
// which do not have class file version.
assert options.testing.enableForceNestBasedAccessDesugaringForTest
- || options.isDesugaredLibraryCompilation()
- || options.cfToCfDesugar;
+ || options.isDesugaredLibraryCompilation()
+ || options.cfToCfDesugar
+ : "Expected class file version for " + method.method.toSourceString();
// TODO(b/146424042): We may call static methods on interface classes so we have to go for
// Java 8.
assert MIN_VERSION_FOR_COMPILER_GENERATED_CODE.isLessThan(CfVersion.V1_8);
@@ -273,10 +277,10 @@
? clazz.getInitialClassFileVersion()
: MIN_VERSION_FOR_COMPILER_GENERATED_CODE;
for (DexEncodedMethod method : clazz.directMethods()) {
- version = version.max(getClassFileVersion(method));
+ version = Ordered.max(version, getClassFileVersion(method));
}
for (DexEncodedMethod method : clazz.virtualMethods()) {
- version = version.max(getClassFileVersion(method));
+ version = Ordered.max(version, getClassFileVersion(method));
}
return version;
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
index 5327873..189a6b0 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
@@ -58,6 +58,10 @@
appView.options().reporter,
onlyProcessLambdas,
method -> keepByteCodeFunctions.add(method.method)));
+ if (onlyProcessLambdas) {
+ clazz.removeAnnotations(
+ annotation -> annotation.getAnnotationType() == kotlinMetadataType);
+ }
if (clazz.getEnclosingMethodAttribute() != null
&& clazz.getEnclosingMethodAttribute().getEnclosingMethod() != null) {
localOrAnonymousClasses.add(clazz);
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index b014c4f..55ed725 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -301,7 +301,7 @@
}
private Set<DexClass> buildSortedPartition(DexClass src) {
- Set<DexClass> partition = new TreeSet<>((x, y) -> x.type.slowCompareTo(y.type));
+ Set<DexClass> partition = new TreeSet<>((x, y) -> x.type.compareTo(y.type));
Deque<DexType> worklist = new ArrayDeque<>();
worklist.add(src.type);
diff --git a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
index e6fcce6..86dac13 100644
--- a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
@@ -264,7 +264,7 @@
// reservation, we have to prioritize that over the others, otherwise we just propose the
// first ordered reserved name since we do not allow overwriting the name.
List<DexEncodedMethod> sortedMethods = Lists.newArrayList(methodStates.keySet());
- sortedMethods.sort((x, y) -> x.getReference().slowCompareTo(y.getReference()));
+ sortedMethods.sort((x, y) -> x.getReference().compareTo(y.getReference()));
DexString reservedName = null;
for (DexEncodedMethod method : sortedMethods) {
for (InterfaceReservationState state : methodStates.get(method)) {
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index b886f2b..9893fb7 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -47,7 +47,7 @@
assert appView.options().isMinifying();
SubtypingInfo subtypingInfo = appView.appInfo().computeSubtypingInfo();
timing.begin("ComputeInterfaces");
- Set<DexClass> interfaces = new TreeSet<>((a, b) -> a.type.slowCompareTo(b.type));
+ Set<DexClass> interfaces = new TreeSet<>((a, b) -> a.type.compareTo(b.type));
interfaces.addAll(appView.appInfo().computeReachableInterfaces());
timing.end();
timing.begin("MinifyClasses");
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index a81ebd7..6d8c889 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -73,7 +73,7 @@
private final SeedMapper seedMapper;
private final BiMap<DexType, DexString> mappedNames = HashBiMap.create();
// To keep the order deterministic, we sort the classes by their type, which is a unique key.
- private final Set<DexClass> mappedClasses = new TreeSet<>((a, b) -> a.type.slowCompareTo(b.type));
+ private final Set<DexClass> mappedClasses = new TreeSet<>((a, b) -> a.type.compareTo(b.type));
private final Map<DexReference, MemberNaming> memberNames = Maps.newIdentityHashMap();
private final Map<DexType, DexString> syntheticCompanionClasses = Maps.newIdentityHashMap();
private final Map<DexMethod, DexString> defaultInterfaceMethodImplementationNames =
@@ -95,7 +95,7 @@
Set<DexReference> notMappedReferences = new HashSet<>();
timing.begin("MappingInterfaces");
- Set<DexClass> interfaces = new TreeSet<>((a, b) -> a.type.slowCompareTo(b.type));
+ Set<DexClass> interfaces = new TreeSet<>((a, b) -> a.type.compareTo(b.type));
Consumer<DexClass> consumer =
dexClass -> {
if (dexClass.isInterface()) {
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
index 23a9a06..49b46cd 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
@@ -221,10 +221,6 @@
skipWhitespace();
// Workaround for proguard map files that contain entries for package-info.java files.
assert IdentifierUtils.isDexIdentifierPart('-');
- if (before.endsWith("package-info")) {
- skipLine();
- continue;
- }
if (before.endsWith("-") && acceptString(">")) {
// With - as a legal identifier part the grammar is ambiguous, and we treat a->b as a -> b,
// and not as a- > b (which would be a parse error).
diff --git a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
index 9b12469..d949ab2 100644
--- a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
@@ -43,7 +43,8 @@
@Override
protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
- assert previous.getReboundReference() == null;
+ assert !previous.hasCastType();
+ assert !previous.hasReboundReference();
return FieldLookupResult.builder(this)
.setReference(previous.getReference())
.setReboundReference(getReboundFieldReference(previous.getReference()))
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index 77301cf..136595a 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -23,9 +23,15 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.BiForEachable;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.TriConsumer;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -160,6 +166,14 @@
BiForEachable<DexMethod, ProgramMethodSet> methodsWithContexts,
Function<DexMethod, DexEncodedMethod> lookupTarget,
Type invokeType) {
+
+ Map<DexProgramClass, List<Pair<DexMethod, DexEncodedMethod>>> bridges = new IdentityHashMap<>();
+ TriConsumer<DexProgramClass, DexMethod, DexEncodedMethod> addBridge =
+ (bridgeHolder, method, target) ->
+ bridges
+ .computeIfAbsent(bridgeHolder, k -> new ArrayList<>())
+ .add(new Pair<>(method, target));
+
methodsWithContexts.forEach(
(method, contexts) -> {
// We can safely ignore array types, as the corresponding methods are defined in a
@@ -178,6 +192,7 @@
return;
}
DexClass targetClass = appView.definitionFor(target.holder());
+ DexMethod targetMethod = target.method;
if (originalClass.isProgramClass()) {
// In Java bytecode, it is only possible to target interface methods that are in one of
// the immediate super-interfaces via a super-invocation (see
@@ -186,9 +201,9 @@
// bridge method when we are about to rebind to an interface method that is not the
// original target.
if (needsBridgeForInterfaceMethod(originalClass, targetClass, invokeType)) {
- target =
+ targetMethod =
insertBridgeForInterfaceMethod(
- method, target, originalClass.asProgramClass(), targetClass, lookupTarget);
+ method, target, originalClass.asProgramClass(), targetClass, addBridge);
}
// If the target class is not public but the targeted method is, we might run into
@@ -196,13 +211,32 @@
final DexEncodedMethod finalTarget = target;
if (contexts.stream()
.anyMatch(context -> mayNeedBridgeForVisibility(context, finalTarget))) {
- target =
+ targetMethod =
insertBridgeForVisibilityIfNeeded(
- method, target, originalClass, targetClass, lookupTarget);
+ method, target, originalClass, targetClass, addBridge);
}
}
lensBuilder.map(
- method, lens.lookupMethod(validTargetFor(target.method, method)), invokeType);
+ method, lens.lookupMethod(validTargetFor(targetMethod, method)), invokeType);
+ });
+
+ bridges.forEach(
+ (bridgeHolder, targets) -> {
+ // Sorting the list of bridges within a class maintains a deterministic order of entries
+ // in the method collection.
+ targets.sort((p1, p2) -> p1.getFirst().compareTo(p2.getFirst()));
+ for (Pair<DexMethod, DexEncodedMethod> pair : targets) {
+ DexMethod method = pair.getFirst();
+ DexEncodedMethod target = pair.getSecond();
+ DexMethod bridgeMethod =
+ method.withHolder(bridgeHolder.getType(), appView.dexItemFactory());
+ if (bridgeHolder.getMethodCollection().getMethod(bridgeMethod) == null) {
+ DexEncodedMethod bridgeMethodDefinition =
+ target.toForwardingMethod(bridgeHolder, appView);
+ bridgeHolder.addMethod(bridgeMethodDefinition);
+ }
+ assert lookupTarget.apply(method).method == bridgeMethod;
+ }
});
}
@@ -214,12 +248,12 @@
&& targetClass.accessFlags.isInterface();
}
- private DexEncodedMethod insertBridgeForInterfaceMethod(
+ private DexMethod insertBridgeForInterfaceMethod(
DexMethod method,
DexEncodedMethod target,
DexProgramClass originalClass,
DexClass targetClass,
- Function<DexMethod, DexEncodedMethod> lookupTarget) {
+ TriConsumer<DexProgramClass, DexMethod, DexEncodedMethod> bridges) {
// If `targetClass` is a class, then insert the bridge method on the upper-most super class that
// implements the interface. Otherwise, if it is an interface, then insert the bridge method
// directly on the interface (because that interface must be the immediate super type, assuming
@@ -232,10 +266,8 @@
findHolderForInterfaceMethodBridge(originalClass, targetClass.type);
assert bridgeHolder != null;
assert bridgeHolder != targetClass;
- DexEncodedMethod bridgeMethod = target.toForwardingMethod(bridgeHolder, appView);
- bridgeHolder.addMethod(bridgeMethod);
- assert lookupTarget.apply(method) == bridgeMethod;
- return bridgeMethod;
+ bridges.accept(bridgeHolder, method, target);
+ return target.method.withHolder(bridgeHolder.getType(), appView.dexItemFactory());
}
private DexProgramClass findHolderForInterfaceMethodBridge(DexProgramClass clazz, DexType iface) {
@@ -267,12 +299,12 @@
&& methodVisibility != ConstraintWithTarget.NEVER;
}
- private DexEncodedMethod insertBridgeForVisibilityIfNeeded(
+ private DexMethod insertBridgeForVisibilityIfNeeded(
DexMethod method,
DexEncodedMethod target,
DexClass originalClass,
DexClass targetClass,
- Function<DexMethod, DexEncodedMethod> lookupTarget) {
+ TriConsumer<DexProgramClass, DexMethod, DexEncodedMethod> bridges) {
// If the original class is public and this method is public, it might have been called
// from anywhere, so we need a bridge. Likewise, if the original is in a different
// package, we might need a bridge, too.
@@ -283,12 +315,10 @@
DexProgramClass bridgeHolder =
findHolderForVisibilityBridge(originalClass, targetClass, packageDescriptor);
assert bridgeHolder != null;
- DexEncodedMethod bridgeMethod = target.toForwardingMethod(bridgeHolder, appView);
- bridgeHolder.addMethod(bridgeMethod);
- assert lookupTarget.apply(method) == bridgeMethod;
- return bridgeMethod;
+ bridges.accept(bridgeHolder, method, target);
+ return target.method.withHolder(bridgeHolder.getType(), appView.dexItemFactory());
}
- return target;
+ return target.method;
}
private DexProgramClass findHolderForVisibilityBridge(
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
index 02b56e3..3f77dbb 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
@@ -51,7 +51,8 @@
@Override
protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
- assert previous.getReboundReference() == null;
+ assert !previous.hasCastType();
+ assert !previous.hasReboundReference();
return FieldLookupResult.builder(this)
.setReference(previous.getReference())
.setReboundReference(getReboundFieldReference(previous.getReference()))
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
index 867ec0b..171dcbe 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
@@ -95,7 +95,8 @@
@Override
protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
- assert previous.getReboundReference() == null;
+ assert !previous.hasCastType();
+ assert !previous.hasReboundReference();
return FieldLookupResult.builder(this)
.setReference(previous.getReference())
.setReboundReference(getReboundFieldReference(previous.getReference()))
diff --git a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
index 0d2c4e8..cea7fc2 100644
--- a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
+++ b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
@@ -113,7 +113,7 @@
private void processClass(DexProgramClass clazz, SubtypingInfo subtypingInfo) {
Set<DexType> subtypes = subtypingInfo.allImmediateSubtypes(clazz.type);
- Set<DexProgramClass> subclasses = new TreeSet<>((x, y) -> x.type.slowCompareTo(y.type));
+ Set<DexProgramClass> subclasses = new TreeSet<>((x, y) -> x.type.compareTo(y.type));
for (DexType subtype : subtypes) {
DexProgramClass subclass = asProgramClassOrNull(appView.definitionFor(subtype));
if (subclass == null) {
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
index 07af4a3..c6a2424 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
@@ -56,7 +56,7 @@
}
appBuilder.replaceProgramClasses(new ArrayList<>(newProgramClasses.values()));
RepackagingLens lens = lensBuilder.build(appView);
- new AnnotationFixer(lens).run(appView.appInfo().classes());
+ new AnnotationFixer(lens).run(newProgramClasses.values());
return lens;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index fc6cb56..9706eab 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -261,7 +261,8 @@
return liveGetter ? original : null;
}
- private boolean enclosingMethodPinned(DexClass clazz) {
+ private static boolean enclosingMethodPinned(
+ AppView<AppInfoWithLiveness> appView, DexClass clazz) {
return clazz.getEnclosingMethodAttribute() != null
&& clazz.getEnclosingMethodAttribute().getEnclosingClass() != null
&& appView.appInfo().isPinned(clazz.getEnclosingMethodAttribute().getEnclosingClass());
@@ -285,7 +286,7 @@
// is kept.
boolean keptAnyway =
appView.appInfo().isPinned(clazz.type)
- || enclosingMethodPinned(clazz)
+ || enclosingMethodPinned(appView, clazz)
|| appView.options().forceProguardCompatibility;
boolean keepForThisInnerClass = false;
boolean keepForThisEnclosingClass = false;
@@ -391,7 +392,7 @@
for (DexProgramClass clazz : appView.appInfo().classes()) {
// If [clazz] is mentioned by a keep rule, it could be used for reflection, and we
// therefore need to keep the enclosing method and inner classes attributes, if requested.
- if (appView.appInfo().isPinned(clazz.type)) {
+ if (appView.appInfo().isPinned(clazz) || enclosingMethodPinned(appView, clazz)) {
for (InnerClassAttribute innerClassAttribute : clazz.getInnerClasses()) {
DexType inner = innerClassAttribute.getInner();
if (appView.appInfo().isNonProgramTypeOrLiveProgramType(inner)) {
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 4d5a549..bdda13b 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -26,13 +26,11 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.EnumValueInfoMapCollection;
-import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfo;
import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
import com.android.tools.r8.graph.FieldAccessInfo;
import com.android.tools.r8.graph.FieldAccessInfoCollection;
import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl;
import com.android.tools.r8.graph.FieldResolutionResult;
-import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.InstantiatedSubTypeInfo;
import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
@@ -40,7 +38,6 @@
import com.android.tools.r8.graph.MethodAccessInfoCollection;
import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
-import com.android.tools.r8.graph.PresortedComparable;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
@@ -60,6 +57,7 @@
import com.android.tools.r8.utils.Visibility;
import com.android.tools.r8.utils.WorkList;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.android.tools.r8.utils.structural.Ordered;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
@@ -70,9 +68,6 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.SortedMap;
-import java.util.SortedSet;
-import java.util.TreeMap;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -89,11 +84,6 @@
*/
private final Set<DexType> liveTypes;
/**
- * Set of service types (from META-INF/services/) that may have been instantiated reflectively via
- * ServiceLoader.load() or ServiceLoader.loadInstalled().
- */
- public final Set<DexType> instantiatedAppServices;
- /**
* Set of methods that are the immediate target of an invoke. They might not actually be live but
* are required so that invokes can find the method. If such a method is not live (i.e. not
* contained in {@link #liveMethods}, it may be marked as abstract and its implementation may be
@@ -148,29 +138,29 @@
/** All methods that *must* never be inlined due to a configuration directive (testing only). */
private final Set<DexMethod> neverInline;
/** Items for which to print inlining decisions for (testing only). */
- public final Set<DexMethod> whyAreYouNotInlining;
+ private final Set<DexMethod> whyAreYouNotInlining;
/** All methods that may not have any parameters with a constant value removed. */
- public final Set<DexMethod> keepConstantArguments;
+ private final Set<DexMethod> keepConstantArguments;
/** All methods that may not have any unused arguments removed. */
- public final Set<DexMethod> keepUnusedArguments;
+ private final Set<DexMethod> keepUnusedArguments;
/** All methods that must be reprocessed (testing only). */
- public final Set<DexMethod> reprocess;
+ private final Set<DexMethod> reprocess;
/** All methods that must not be reprocessed (testing only). */
- public final Set<DexMethod> neverReprocess;
+ private final Set<DexMethod> neverReprocess;
/** All types that should be inlined if possible due to a configuration directive. */
public final PredicateSet<DexType> alwaysClassInline;
/** All types that *must* never be inlined due to a configuration directive (testing only). */
- public final Set<DexType> neverClassInline;
+ private final Set<DexType> neverClassInline;
- private final Set<DexType> noUnusedInterfaceRemoval;
- private final Set<DexType> noVerticalClassMerging;
+ private final Set<DexType> noClassMerging;
private final Set<DexType> noHorizontalClassMerging;
+ private final Set<DexType> noVerticalClassMerging;
private final Set<DexType> noStaticClassMerging;
/**
* Set of lock candidates (i.e., types whose class reference may flow to a monitor instruction).
*/
- public final Set<DexType> lockCandidates;
+ private final Set<DexType> lockCandidates;
/**
* A map from seen init-class references to the minimum required visibility of the corresponding
* static field.
@@ -205,7 +195,6 @@
Set<DexType> deadProtoTypes,
Set<DexType> missingTypes,
Set<DexType> liveTypes,
- Set<DexType> instantiatedAppServices,
Set<DexMethod> targetedMethods,
Set<DexMethod> failedResolutionTargets,
Set<DexMethod> bootstrapMethods,
@@ -230,7 +219,7 @@
Set<DexMethod> neverReprocess,
PredicateSet<DexType> alwaysClassInline,
Set<DexType> neverClassInline,
- Set<DexType> noUnusedInterfaceRemoval,
+ Set<DexType> noClassMerging,
Set<DexType> noVerticalClassMerging,
Set<DexType> noHorizontalClassMerging,
Set<DexType> noStaticClassMerging,
@@ -245,7 +234,6 @@
this.deadProtoTypes = deadProtoTypes;
this.missingTypes = missingTypes;
this.liveTypes = liveTypes;
- this.instantiatedAppServices = instantiatedAppServices;
this.targetedMethods = targetedMethods;
this.failedResolutionTargets = failedResolutionTargets;
this.bootstrapMethods = bootstrapMethods;
@@ -270,93 +258,7 @@
this.neverReprocess = neverReprocess;
this.alwaysClassInline = alwaysClassInline;
this.neverClassInline = neverClassInline;
- this.noUnusedInterfaceRemoval = noUnusedInterfaceRemoval;
- this.noVerticalClassMerging = noVerticalClassMerging;
- this.noHorizontalClassMerging = noHorizontalClassMerging;
- this.noStaticClassMerging = noStaticClassMerging;
- this.neverPropagateValue = neverPropagateValue;
- this.identifierNameStrings = identifierNameStrings;
- this.prunedTypes = prunedTypes;
- this.switchMaps = switchMaps;
- this.enumValueInfoMaps = enumValueInfoMaps;
- this.lockCandidates = lockCandidates;
- this.initClassReferences = initClassReferences;
- }
-
- public AppInfoWithLiveness(
- AppInfoWithClassHierarchy appInfoWithClassHierarchy,
- Set<DexType> deadProtoTypes,
- Set<DexType> missingTypes,
- Set<DexType> liveTypes,
- Set<DexType> instantiatedAppServices,
- Set<DexMethod> targetedMethods,
- Set<DexMethod> failedResolutionTargets,
- Set<DexMethod> bootstrapMethods,
- Set<DexMethod> methodsTargetedByInvokeDynamic,
- SortedSet<DexMethod> virtualMethodsTargetedByInvokeDirect,
- SortedSet<DexMethod> liveMethods,
- FieldAccessInfoCollectionImpl fieldAccessInfoCollection,
- MethodAccessInfoCollection methodAccessInfoCollection,
- ObjectAllocationInfoCollectionImpl objectAllocationInfoCollection,
- Map<DexCallSite, ProgramMethodSet> callSites,
- KeepInfoCollection keepInfo,
- Map<DexReference, ProguardMemberRule> mayHaveSideEffects,
- Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects,
- Map<DexMember<?, ?>, ProguardMemberRule> assumedValues,
- Set<DexMethod> alwaysInline,
- Set<DexMethod> forceInline,
- Set<DexMethod> neverInline,
- Set<DexMethod> whyAreYouNotInlining,
- Set<DexMethod> keepConstantArguments,
- Set<DexMethod> keepUnusedArguments,
- Set<DexMethod> reprocess,
- Set<DexMethod> neverReprocess,
- PredicateSet<DexType> alwaysClassInline,
- Set<DexType> neverClassInline,
- Set<DexType> noUnusedInterfaceRemoval,
- Set<DexType> noVerticalClassMerging,
- Set<DexType> noHorizontalClassMerging,
- Set<DexType> noStaticClassMerging,
- Set<DexReference> neverPropagateValue,
- Object2BooleanMap<DexReference> identifierNameStrings,
- Set<DexType> prunedTypes,
- Map<DexField, Int2ReferenceMap<DexField>> switchMaps,
- EnumValueInfoMapCollection enumValueInfoMaps,
- Set<DexType> lockCandidates,
- Map<DexType, Visibility> initClassReferences) {
- super(
- appInfoWithClassHierarchy.getSyntheticItems().commit(appInfoWithClassHierarchy.app()),
- appInfoWithClassHierarchy.getClassToFeatureSplitMap(),
- appInfoWithClassHierarchy.getMainDexClasses());
- this.deadProtoTypes = deadProtoTypes;
- this.missingTypes = missingTypes;
- this.liveTypes = liveTypes;
- this.instantiatedAppServices = instantiatedAppServices;
- this.targetedMethods = targetedMethods;
- this.failedResolutionTargets = failedResolutionTargets;
- this.bootstrapMethods = bootstrapMethods;
- this.methodsTargetedByInvokeDynamic = methodsTargetedByInvokeDynamic;
- this.virtualMethodsTargetedByInvokeDirect = virtualMethodsTargetedByInvokeDirect;
- this.liveMethods = liveMethods;
- this.fieldAccessInfoCollection = fieldAccessInfoCollection;
- this.methodAccessInfoCollection = methodAccessInfoCollection;
- this.objectAllocationInfoCollection = objectAllocationInfoCollection;
- this.keepInfo = keepInfo;
- this.mayHaveSideEffects = mayHaveSideEffects;
- this.noSideEffects = noSideEffects;
- this.assumedValues = assumedValues;
- this.callSites = callSites;
- this.alwaysInline = alwaysInline;
- this.forceInline = forceInline;
- this.neverInline = neverInline;
- this.whyAreYouNotInlining = whyAreYouNotInlining;
- this.keepConstantArguments = keepConstantArguments;
- this.keepUnusedArguments = keepUnusedArguments;
- this.reprocess = reprocess;
- this.neverReprocess = neverReprocess;
- this.alwaysClassInline = alwaysClassInline;
- this.neverClassInline = neverClassInline;
- this.noUnusedInterfaceRemoval = noUnusedInterfaceRemoval;
+ this.noClassMerging = noClassMerging;
this.noVerticalClassMerging = noVerticalClassMerging;
this.noHorizontalClassMerging = noHorizontalClassMerging;
this.noStaticClassMerging = noStaticClassMerging;
@@ -379,7 +281,6 @@
previous.missingTypes,
CollectionUtils.mergeSets(
Sets.difference(previous.liveTypes, removedTypes), committedItems.getCommittedTypes()),
- previous.instantiatedAppServices,
previous.targetedMethods,
previous.failedResolutionTargets,
previous.bootstrapMethods,
@@ -404,7 +305,7 @@
previous.neverReprocess,
previous.alwaysClassInline,
previous.neverClassInline,
- previous.noUnusedInterfaceRemoval,
+ previous.noClassMerging,
previous.noVerticalClassMerging,
previous.noHorizontalClassMerging,
previous.noStaticClassMerging,
@@ -431,7 +332,6 @@
removedClasses == null
? previous.liveTypes
: Sets.difference(previous.liveTypes, removedClasses),
- previous.instantiatedAppServices,
previous.targetedMethods,
previous.failedResolutionTargets,
previous.bootstrapMethods,
@@ -456,7 +356,7 @@
previous.neverReprocess,
previous.alwaysClassInline,
previous.neverClassInline,
- previous.noUnusedInterfaceRemoval,
+ previous.noClassMerging,
previous.noVerticalClassMerging,
previous.noHorizontalClassMerging,
previous.noStaticClassMerging,
@@ -520,7 +420,6 @@
this.deadProtoTypes = previous.deadProtoTypes;
this.missingTypes = previous.missingTypes;
this.liveTypes = previous.liveTypes;
- this.instantiatedAppServices = previous.instantiatedAppServices;
this.targetedMethods = previous.targetedMethods;
this.failedResolutionTargets = previous.failedResolutionTargets;
this.bootstrapMethods = previous.bootstrapMethods;
@@ -545,7 +444,7 @@
this.neverReprocess = previous.neverReprocess;
this.alwaysClassInline = previous.alwaysClassInline;
this.neverClassInline = previous.neverClassInline;
- this.noUnusedInterfaceRemoval = previous.noUnusedInterfaceRemoval;
+ this.noClassMerging = previous.noClassMerging;
this.noVerticalClassMerging = previous.noVerticalClassMerging;
this.noHorizontalClassMerging = previous.noHorizontalClassMerging;
this.noStaticClassMerging = previous.noStaticClassMerging;
@@ -601,7 +500,7 @@
// Skip synthetic classes which may not have a specified version.
if (clazz.hasClassFileVersion()) {
largestInputCfVersion =
- CfVersion.maxAllowNull(largestInputCfVersion, clazz.getInitialClassFileVersion());
+ Ordered.maxIgnoreNull(largestInputCfVersion, clazz.getInitialClassFileVersion());
}
}
assert largestInputCfVersion != null;
@@ -675,6 +574,30 @@
return neverInline.contains(method);
}
+ public boolean isWhyAreYouNotInliningMethod(DexMethod method) {
+ return whyAreYouNotInlining.contains(method);
+ }
+
+ public boolean hasNoWhyAreYouNotInliningMethods() {
+ return whyAreYouNotInlining.isEmpty();
+ }
+
+ public boolean isKeepConstantArgumentsMethod(DexMethod method) {
+ return keepConstantArguments.contains(method);
+ }
+
+ public boolean isKeepUnusedArgumentsMethod(DexMethod method) {
+ return keepUnusedArguments.contains(method);
+ }
+
+ public boolean isNeverReprocessMethod(DexMethod method) {
+ return neverReprocess.contains(method);
+ }
+
+ public Set<DexMethod> getReprocessMethods() {
+ return reprocess;
+ }
+
public Collection<DexClass> computeReachableInterfaces() {
Set<DexClass> interfaces = Sets.newIdentityHashSet();
WorkList<DexType> worklist = WorkList.newIdentityWorkList();
@@ -780,12 +703,6 @@
return enumValueInfoMaps.getEnumValueInfoMap(enumType);
}
- public EnumValueInfo getEnumValueInfo(DexField field) {
- assert checkIfObsolete();
- EnumValueInfoMap map = enumValueInfoMaps.getEnumValueInfoMap(field.type);
- return map != null ? map.getEnumValueInfo(field) : null;
- }
-
public Int2ReferenceMap<DexField> getSwitchMap(DexField field) {
assert checkIfObsolete();
return switchMaps.get(field);
@@ -936,18 +853,6 @@
return holder == null || holder.isLibraryClass() || holder.isClasspathClass();
}
- private static SortedMap<DexMethod, ProgramMethodSet> rewriteInvokesWithContexts(
- Map<DexMethod, ProgramMethodSet> invokes, GraphLens lens) {
- SortedMap<DexMethod, ProgramMethodSet> result = new TreeMap<>(PresortedComparable::slowCompare);
- invokes.forEach(
- (method, contexts) ->
- result
- .computeIfAbsent(
- lens.getRenamedMethodSignature(method), ignore -> ProgramMethodSet.create())
- .addAll(contexts));
- return Collections.unmodifiableSortedMap(result);
- }
-
public boolean isInstantiatedInterface(DexProgramClass clazz) {
assert checkIfObsolete();
return objectAllocationInfoCollection.isInterfaceWithUnknownSubtypeHierarchy(clazz);
@@ -1067,7 +972,6 @@
deadProtoTypes,
missingTypes,
lens.rewriteTypes(liveTypes),
- lens.rewriteTypes(instantiatedAppServices),
lens.rewriteMethods(targetedMethods),
lens.rewriteMethods(failedResolutionTargets),
lens.rewriteMethods(bootstrapMethods),
@@ -1085,14 +989,14 @@
lens.rewriteMethods(alwaysInline),
lens.rewriteMethods(forceInline),
lens.rewriteMethods(neverInline),
- lens.rewriteMethodsSorted(whyAreYouNotInlining),
- lens.rewriteMethodsSorted(keepConstantArguments),
- lens.rewriteMethodsSorted(keepUnusedArguments),
- lens.rewriteMethodsSorted(reprocess),
- lens.rewriteMethodsSorted(neverReprocess),
+ lens.rewriteMethods(whyAreYouNotInlining),
+ lens.rewriteMethods(keepConstantArguments),
+ lens.rewriteMethods(keepUnusedArguments),
+ lens.rewriteMethods(reprocess),
+ lens.rewriteMethods(neverReprocess),
alwaysClassInline.rewriteItems(lens::lookupType),
lens.rewriteTypes(neverClassInline),
- lens.rewriteTypes(noUnusedInterfaceRemoval),
+ lens.rewriteTypes(noClassMerging),
lens.rewriteTypes(noVerticalClassMerging),
lens.rewriteTypes(noHorizontalClassMerging),
lens.rewriteTypes(noStaticClassMerging),
@@ -1463,25 +1367,14 @@
.shouldBreak();
}
- /** All unused interface types that *must* never be pruned. */
- public Set<DexType> getNoUnusedInterfaceRemovalSet() {
- return noUnusedInterfaceRemoval;
+ /** Predicate on types that *must* never be merged horizontally. */
+ public boolean isNoHorizontalClassMergingOfType(DexType type) {
+ return noClassMerging.contains(type) || noHorizontalClassMerging.contains(type);
}
- /**
- * All types that *must* never be merged vertically due to a configuration directive (testing
- * only).
- */
- public Set<DexType> getNoVerticalClassMergingSet() {
- return noVerticalClassMerging;
- }
-
- /**
- * All types that *must* never be merged horizontally due to a configuration directive (testing
- * only).
- */
- public Set<DexType> getNoHorizontalClassMergingSet() {
- return noHorizontalClassMerging;
+ /** Predicate on types that *must* never be merged vertically. */
+ public boolean isNoVerticalClassMergingOfType(DexType type) {
+ return noClassMerging.contains(type) || noVerticalClassMerging.contains(type);
}
/**
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index e032ea2..ed31df5 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -208,8 +208,8 @@
private final FieldAccessInfoCollectionImpl fieldAccessInfoCollection =
new FieldAccessInfoCollectionImpl();
- private final MethodAccessInfoCollection.SortedBuilder methodAccessInfoCollection =
- MethodAccessInfoCollection.sortedBuilder();
+ private final MethodAccessInfoCollection.IdentityBuilder methodAccessInfoCollection =
+ MethodAccessInfoCollection.identityBuilder();
private final ObjectAllocationInfoCollectionImpl.Builder objectAllocationInfoCollection;
private final Map<DexCallSite, ProgramMethodSet> callSites = new IdentityHashMap<>();
@@ -271,6 +271,8 @@
/** Set of types that was pruned during the first round of tree shaking. */
private Set<DexType> initialPrunedTypes;
+ private final Set<DexType> noClassMerging = Sets.newIdentityHashSet();
+
/** Mapping from each unused interface to the set of live types that implements the interface. */
private final Map<DexProgramClass, Set<DexProgramClass>> unusedInterfaceTypes =
new IdentityHashMap<>();
@@ -308,12 +310,6 @@
*/
private final LiveFieldsSet liveFields;
- /**
- * Set of service types (from META-INF/services/) that may have been instantiated reflectively via
- * ServiceLoader.load() or ServiceLoader.loadInstalled().
- */
- private final Set<DexType> instantiatedAppServices = Sets.newIdentityHashSet();
-
/** A queue of items that need processing. Different items trigger different actions. */
private final EnqueuerWorklist workList;
@@ -1360,6 +1356,7 @@
FieldResolutionResult resolutionResult = resolveField(fieldReference);
if (resolutionResult.isFailedOrUnknownResolution()) {
+ noClassMerging.add(fieldReference.getHolderType());
return;
}
@@ -1410,6 +1407,7 @@
FieldResolutionResult resolutionResult = resolveField(fieldReference);
if (resolutionResult.isFailedOrUnknownResolution()) {
+ noClassMerging.add(fieldReference.getHolderType());
return;
}
@@ -1460,6 +1458,7 @@
if (resolutionResult.isFailedOrUnknownResolution()) {
// Must mark the field as targeted even if it does not exist.
markFieldAsTargeted(fieldReference, currentMethod);
+ noClassMerging.add(fieldReference.getHolderType());
return;
}
@@ -1518,6 +1517,7 @@
if (resolutionResult.isFailedOrUnknownResolution()) {
// Must mark the field as targeted even if it does not exist.
markFieldAsTargeted(fieldReference, currentMethod);
+ noClassMerging.add(fieldReference.getHolderType());
return;
}
@@ -3242,7 +3242,6 @@
? Sets.union(initialMissingTypes, missingTypes)
: missingTypes,
SetUtils.mapIdentityHashSet(liveTypes.getItems(), DexProgramClass::getType),
- Collections.unmodifiableSet(instantiatedAppServices),
Enqueuer.toDescriptorSet(targetedMethods.getItems()),
Collections.unmodifiableSet(failedResolutionTargets),
Collections.unmodifiableSet(bootstrapMethods),
@@ -3268,7 +3267,7 @@
rootSet.neverReprocess,
rootSet.alwaysClassInline,
rootSet.neverClassInline,
- rootSet.noUnusedInterfaceRemoval,
+ noClassMerging,
rootSet.noVerticalClassMerging,
rootSet.noHorizontalClassMerging,
rootSet.noStaticClassMerging,
@@ -3925,6 +3924,11 @@
} else if (identifierTypeLookupResult.isTypeInitializedFromUse()) {
markDirectAndIndirectClassInitializersAsLive(clazz);
}
+ // To ensure we are not moving the class because we cannot prune it when there is a reflective
+ // use of it.
+ if (!keepInfo.getClassInfo(clazz).isPinned()) {
+ keepInfo.pinClass(clazz);
+ }
} else if (referencedItem.isDexField()) {
DexField field = referencedItem.asDexField();
DexProgramClass clazz = getProgramClassOrNull(field.holder);
@@ -4200,8 +4204,6 @@
}
private void handleServiceInstantiation(DexType serviceType, KeepReason reason) {
- instantiatedAppServices.add(serviceType);
-
List<DexType> serviceImplementationTypes =
appView.appServices().serviceImplementationsFor(serviceType);
for (DexType serviceImplementationType : serviceImplementationTypes) {
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
index 7d75f05..36f5cdd 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
@@ -84,8 +84,9 @@
public boolean isAllowSignatureAttributeRemovalAllowed(
GlobalKeepInfoConfiguration configuration) {
- return !configuration.isKeepAttributesSignatureEnabled()
- || !(isPinned() || configuration.isForceProguardCompatibilityEnabled());
+ // TODO(b/172999267): For full mode we should be able to remove for not pinned items if
+ // java reflect will not throw up.
+ return !configuration.isKeepAttributesSignatureEnabled();
}
public abstract boolean isTop();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
index 4a19c6f..aac168d 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
@@ -105,6 +105,10 @@
AppView<? extends AppInfoWithClassHierarchy> appView,
SubtypingInfo subtypingInfo,
Iterable<DexProgramClass> defaultValue) {
+ List<DexType> specificTypes = getClassNames().asSpecificDexTypes();
+ if (specificTypes != null) {
+ return DexProgramClass.asProgramClasses(specificTypes, appView);
+ }
if (hasInheritanceClassName() && getInheritanceClassName().hasSpecificType()) {
DexType type = getInheritanceClassName().getSpecificType();
if (appView.verticallyMergedClasses() != null
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 7267bac..1d476bf 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -745,7 +745,7 @@
DexProgramClass holder = asProgramClassOrNull(appInfo.definitionForHolder(method));
DexEncodedMethod definition = method.lookupOnClass(holder);
if (definition == null) {
- assert false;
+ assert method.match(appInfo.dexItemFactory().deserializeLambdaMethod);
return;
}
out.print(method.holder.toSourceString() + ": ");
@@ -1705,7 +1705,7 @@
assert method.getHolderType() == options.dexItemFactory().objectType;
OriginWithPosition key = new OriginWithPosition(context.getOrigin(), context.getPosition());
assumeNoSideEffectsWarnings
- .computeIfAbsent(key, ignore -> new TreeSet<>(DexMethod::slowCompareTo))
+ .computeIfAbsent(key, ignore -> new TreeSet<>(DexMethod::compareTo))
.add(method.getReference());
}
diff --git a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
index 2a72863..97e980c 100644
--- a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
+import com.android.tools.r8.graph.classmerging.StaticallyMergedClasses;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.shaking.VerticalClassMerger.IllegalAccessDetector;
import com.android.tools.r8.utils.FieldSignatureEquivalence;
@@ -188,6 +189,8 @@
private final AppView<AppInfoWithLiveness> appView;
private final MainDexTracingResult mainDexClasses;
+ private final StaticallyMergedClasses.Builder mergedClassesBuilder =
+ StaticallyMergedClasses.builder();
/** The equivalence that should be used for the member buckets in {@link Representative}. */
private final Equivalence<DexField> fieldEquivalence;
@@ -224,6 +227,7 @@
numberOfMergedClasses,
fieldMapping.size() + methodMapping.size());
}
+ appView.setStaticallyMergedClasses(mergedClassesBuilder.build());
return buildGraphLens();
}
@@ -337,7 +341,7 @@
// We are not allowed to merge synchronized classes with synchronized methods.
return;
}
- if (appView.appInfo().lockCandidates.contains(clazz.type)) {
+ if (appView.appInfo().isLockCandidate(clazz.type)) {
// Since the type is const-class referenced (and the static merger does not create a lens
// to map the merged type) the class will likely remain and there is no gain from merging.
return;
@@ -456,6 +460,7 @@
assert getClassToFeatureSplitMap().isInSameFeatureOrBothInBase(sourceClass, targetClass);
numberOfMergedClasses++;
+ mergedClassesBuilder.recordMerge(sourceClass, targetClass);
// Move members from source to target.
targetClass.addDirectMethods(
diff --git a/src/main/java/com/android/tools/r8/shaking/UnusedItemsPrinter.java b/src/main/java/com/android/tools/r8/shaking/UnusedItemsPrinter.java
index 1d57c73..5b89abf 100644
--- a/src/main/java/com/android/tools/r8/shaking/UnusedItemsPrinter.java
+++ b/src/main/java/com/android/tools/r8/shaking/UnusedItemsPrinter.java
@@ -24,8 +24,8 @@
}
void sort() {
- fields.sort((a, b) -> a.getReference().slowCompareTo(b.getReference()));
- methods.sort((a, b) -> a.getReference().slowCompareTo(b.getReference()));
+ fields.sort((a, b) -> a.getReference().compareTo(b.getReference()));
+ methods.sort((a, b) -> a.getReference().compareTo(b.getReference()));
}
}
@@ -73,7 +73,7 @@
}
public void finished() {
- classes.sort((a, b) -> a.getFirst().slowCompareTo(b.getFirst()));
+ classes.sort((a, b) -> a.getFirst().compareTo(b.getFirst()));
for (Pair<DexType, Members> entry : classes) {
DexType type = entry.getFirst();
Members members = entry.getSecond();
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index a294f1d..c98e345 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMember;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -57,6 +58,7 @@
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.TraversalContinuation;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
import com.google.common.base.Equivalence;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.Iterables;
@@ -204,16 +206,14 @@
private final Set<DexProgramClass> mergeCandidates = new LinkedHashSet<>();
// Map from source class to target class.
- private final Map<DexType, DexType> mergedClasses = new IdentityHashMap<>();
-
- // Map from target class to the super classes that have been merged into the target class.
- private final Map<DexType, Set<DexType>> mergedClassesInverse = new IdentityHashMap<>();
+ private final BidirectionalManyToOneMap<DexType, DexType> mergedClasses =
+ new BidirectionalManyToOneMap<>();
// Set of types that must not be merged into their subtype.
private final Set<DexType> pinnedTypes = Sets.newIdentityHashSet();
// The resulting graph lens that should be used after class merging.
- private final VerticalClassMergerGraphLens.Builder renamedMembersLens;
+ private final VerticalClassMergerGraphLens.Builder lensBuilder;
// All the bridge methods that have been synthesized during vertical class merging.
private final List<SynthesizedBridgeCode> synthesizedBridges = new ArrayList<>();
@@ -232,7 +232,7 @@
this.subtypingInfo = appInfo.computeSubtypingInfo();
this.executorService = executorService;
this.methodPoolCollection = new MethodPoolCollection(appView, subtypingInfo);
- this.renamedMembersLens = new VerticalClassMergerGraphLens.Builder(appView.dexItemFactory());
+ this.lensBuilder = new VerticalClassMergerGraphLens.Builder(appView.dexItemFactory());
this.timing = timing;
this.mainDexClasses = mainDexClasses;
@@ -241,10 +241,6 @@
initializeMergeCandidates(classes);
}
- private VerticallyMergedClasses getMergedClasses() {
- return new VerticallyMergedClasses(mergedClasses, mergedClassesInverse);
- }
-
private void initializeMergeCandidates(Iterable<DexProgramClass> classes) {
for (DexProgramClass sourceClass : classes) {
DexType singleSubtype = subtypingInfo.getSingleDirectSubtype(sourceClass.type);
@@ -351,7 +347,7 @@
|| allocationInfo.isImmediateInterfaceOfInstantiatedLambda(sourceClass)
|| appInfo.isPinned(sourceClass.type)
|| pinnedTypes.contains(sourceClass.type)
- || appInfo.getNoVerticalClassMergingSet().contains(sourceClass.type)) {
+ || appInfo.isNoVerticalClassMergingOfType(sourceClass.type)) {
return false;
}
@@ -427,7 +423,7 @@
// before merging [clazz] into its subtype.
private boolean isStillMergeCandidate(DexProgramClass sourceClass, DexProgramClass targetClass) {
assert isMergeCandidate(sourceClass, targetClass, pinnedTypes);
- if (mergedClassesInverse.containsKey(sourceClass.type)) {
+ if (mergedClasses.containsValue(sourceClass.getType())) {
// Do not allow merging the resulting class into its subclass.
// TODO(christofferqa): Get rid of this limitation.
if (Log.ENABLED) {
@@ -441,7 +437,7 @@
// their clinit called - except when the interface has a default method.
if ((sourceClass.hasClassInitializer() && targetClass.hasClassInitializer())
|| targetClass.classInitializationMayHaveSideEffects(
- appView, type -> type == sourceClass.type, Sets.newIdentityHashSet())
+ appView, type -> type == sourceClass.type)
|| (sourceClass.isInterface()
&& sourceClass.classInitializationMayHaveSideEffects(appView))) {
// TODO(herhut): Handle class initializers.
@@ -644,16 +640,20 @@
TopDownClassHierarchyTraversal.forProgramClasses(appView)
.visit(mergeCandidates, this::mergeClassIfPossible);
if (Log.ENABLED) {
- Log.debug(getClass(), "Merged %d classes.", mergedClasses.size());
+ Log.debug(getClass(), "Merged %d classes.", mergedClasses.keySet().size());
}
timing.end();
- if (mergedClasses.isEmpty()) {
+ VerticallyMergedClasses verticallyMergedClasses = new VerticallyMergedClasses(mergedClasses);
+ appView.setVerticallyMergedClasses(verticallyMergedClasses);
+ if (verticallyMergedClasses.isEmpty()) {
return null;
}
timing.begin("fixup");
- VerticalClassMergerGraphLens lens = new TreeFixer().fixupTypeReferences();
+ VerticalClassMergerGraphLens lens =
+ new TreeFixer(appView, lensBuilder, verticallyMergedClasses, synthesizedBridges)
+ .fixupTypeReferences();
KeepInfoCollection keepInfo = appView.appInfo().getKeepInfo();
keepInfo.mutate(mutator -> mutator.removeKeepInfoForPrunedItems(mergedClasses.keySet()));
timing.end();
@@ -806,8 +806,7 @@
assert !mergedClasses.containsKey(targetClass.type);
boolean clazzOrTargetClassHasBeenMerged =
- mergedClassesInverse.containsKey(clazz.type)
- || mergedClassesInverse.containsKey(targetClass.type);
+ mergedClasses.containsValue(clazz.type) || mergedClasses.containsValue(targetClass.type);
if (clazzOrTargetClassHasBeenMerged) {
if (!isStillMergeCandidate(clazz, targetClass)) {
return;
@@ -851,7 +850,7 @@
}
if (merged) {
// Commit the changes to the graph lens.
- renamedMembersLens.merge(merger.getRenamings());
+ lensBuilder.merge(merger.getRenamings());
synthesizedBridges.addAll(merger.getSynthesizedBridges());
}
if (Log.ENABLED) {
@@ -1116,7 +1115,6 @@
source.setStaticFields(null);
// Step 4: Record merging.
mergedClasses.put(source.type, target.type);
- mergedClassesInverse.computeIfAbsent(target.type, key -> new HashSet<>()).add(source.type);
assert !abortMerge;
return true;
}
@@ -1190,23 +1188,21 @@
// the code above. However, instructions on the form "invoke-super A.m()" should also be
// changed into "invoke-direct D.m$C()". This is achieved by also considering the classes
// that have been merged into [holder].
- Set<DexType> mergedTypes = mergedClassesInverse.get(holder.type);
- if (mergedTypes != null) {
- for (DexType type : mergedTypes) {
- DexMethod signatureInType =
- application.dexItemFactory.createMethod(type, oldTarget.proto, oldTarget.name);
- // Resolution would have succeeded if the method used to be in [type], or if one of
- // its super classes declared the method.
- boolean resolutionSucceededBeforeMerge =
- renamedMembersLens.hasMappingForSignatureInContext(holder, signatureInType)
- || appInfo.lookupSuperTarget(signatureInHolder, holder) != null;
- if (resolutionSucceededBeforeMerge) {
- deferredRenamings.mapVirtualMethodToDirectInType(
- signatureInType,
- prototypeChanges ->
- new MethodLookupResult(newTarget, null, DIRECT, prototypeChanges),
- target.type);
- }
+ Set<DexType> mergedTypes = mergedClasses.getKeys(holder.type);
+ for (DexType type : mergedTypes) {
+ DexMethod signatureInType =
+ application.dexItemFactory.createMethod(type, oldTarget.proto, oldTarget.name);
+ // Resolution would have succeeded if the method used to be in [type], or if one of
+ // its super classes declared the method.
+ boolean resolutionSucceededBeforeMerge =
+ lensBuilder.hasMappingForSignatureInContext(holder, signatureInType)
+ || appInfo.lookupSuperTarget(signatureInHolder, holder) != null;
+ if (resolutionSucceededBeforeMerge) {
+ deferredRenamings.mapVirtualMethodToDirectInType(
+ signatureInType,
+ prototypeChanges ->
+ new MethodLookupResult(newTarget, null, DIRECT, prototypeChanges),
+ target.type);
}
}
holder =
@@ -1469,16 +1465,32 @@
method.accessFlags.setPrivate();
}
- private class TreeFixer {
+ private static class TreeFixer {
- private final VerticalClassMergerGraphLens.Builder lensBuilder =
- VerticalClassMergerGraphLens.Builder.createBuilderForFixup(
- renamedMembersLens, mergedClasses);
+ private final AppView<AppInfoWithLiveness> appView;
+ private final DexItemFactory dexItemFactory;
+ private final VerticalClassMergerGraphLens.Builder lensBuilder;
+ private final VerticallyMergedClasses mergedClasses;
+ private final List<SynthesizedBridgeCode> synthesizedBridges;
+
private final Map<DexProto, DexProto> protoFixupCache = new IdentityHashMap<>();
+ TreeFixer(
+ AppView<AppInfoWithLiveness> appView,
+ VerticalClassMergerGraphLens.Builder lensBuilder,
+ VerticallyMergedClasses mergedClasses,
+ List<SynthesizedBridgeCode> synthesizedBridges) {
+ this.appView = appView;
+ this.dexItemFactory = appView.dexItemFactory();
+ this.lensBuilder =
+ VerticalClassMergerGraphLens.Builder.createBuilderForFixup(lensBuilder, mergedClasses);
+ this.mergedClasses = mergedClasses;
+ this.synthesizedBridges = synthesizedBridges;
+ }
+
private VerticalClassMergerGraphLens fixupTypeReferences() {
// Globally substitute merged class types in protos and holders.
- for (DexProgramClass clazz : appInfo.classes()) {
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
clazz.getMethodCollection().replaceMethods(this::fixupMethod);
fixupFields(clazz.staticFields(), clazz::setStaticField);
fixupFields(clazz.instanceFields(), clazz::setInstanceField);
@@ -1486,7 +1498,7 @@
for (SynthesizedBridgeCode synthesizedBridge : synthesizedBridges) {
synthesizedBridge.updateMethodSignatures(this::fixupMethod);
}
- VerticalClassMergerGraphLens lens = lensBuilder.build(appView, getMergedClasses());
+ VerticalClassMergerGraphLens lens = lensBuilder.build(appView, mergedClasses);
if (lens != null) {
new AnnotationFixer(lens).run(appView.appInfo().classes());
}
@@ -1523,7 +1535,7 @@
DexField field = encodedField.field;
DexType newType = fixupType(field.type);
DexType newHolder = fixupType(field.holder);
- DexField newField = application.dexItemFactory.createField(newHolder, newType, field.name);
+ DexField newField = dexItemFactory.createField(newHolder, newType, field.name);
if (newField != encodedField.field) {
if (!lensBuilder.hasOriginalSignatureMappingFor(newField)) {
lensBuilder.map(field, newField);
@@ -1534,7 +1546,7 @@
}
private DexMethod fixupMethod(DexMethod method) {
- return application.dexItemFactory.createMethod(
+ return dexItemFactory.createMethod(
fixupType(method.holder), fixupProto(method.proto), method.name);
}
@@ -1543,7 +1555,7 @@
if (result == null) {
DexType returnType = fixupType(proto.returnType);
DexType[] arguments = fixupTypes(proto.parameters.values);
- result = application.dexItemFactory.createProto(returnType, arguments);
+ result = dexItemFactory.createProto(returnType, arguments);
protoFixupCache.put(proto, result);
}
return result;
@@ -1551,16 +1563,16 @@
private DexType fixupType(DexType type) {
if (type.isArrayType()) {
- DexType base = type.toBaseType(application.dexItemFactory);
+ DexType base = type.toBaseType(dexItemFactory);
DexType fixed = fixupType(base);
if (base == fixed) {
return type;
}
- return type.replaceBaseType(fixed, application.dexItemFactory);
+ return type.replaceBaseType(fixed, dexItemFactory);
}
if (type.isClassType()) {
- while (mergedClasses.containsKey(type)) {
- type = mergedClasses.get(type);
+ while (mergedClasses.hasBeenMergedIntoSubtype(type)) {
+ type = mergedClasses.getTargetFor(type);
}
}
return type;
@@ -1713,7 +1725,7 @@
return AbortReason.UNSAFE_INLINING;
}
- private class SingleTypeMapperGraphLens extends NonIdentityGraphLens {
+ public class SingleTypeMapperGraphLens extends NonIdentityGraphLens {
private final DexType source;
private final DexProgramClass target;
@@ -1770,7 +1782,7 @@
// changed if the method was publicized by ClassAndMemberPublicizer).
MethodLookupResult lookup = appView.graphLens().lookupMethod(method, context, type);
// Then check if there is a renaming due to the vertical class merger.
- DexMethod newMethod = renamedMembersLens.methodMap.get(lookup.getReference());
+ DexMethod newMethod = lensBuilder.methodMap.get(lookup.getReference());
if (newMethod == null) {
return lookup;
}
@@ -1806,7 +1818,7 @@
@Override
public DexField lookupField(DexField field) {
- return renamedMembersLens.fieldMap.getOrDefault(field, field);
+ return lensBuilder.fieldMap.getOrDefault(field, field);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
index a5481fd..2bee133 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
@@ -92,10 +92,6 @@
this.originalMethodSignaturesForBridges = originalMethodSignaturesForBridges;
}
- public VerticallyMergedClasses getMergedClasses() {
- return mergedClasses;
- }
-
@Override
protected Iterable<DexType> internalGetOriginalTypes(DexType previous) {
Collection<DexType> originalTypes = mergedClasses.getSourcesFor(previous);
@@ -185,7 +181,7 @@
this.dexItemFactory = dexItemFactory;
}
- static Builder createBuilderForFixup(Builder builder, Map<DexType, DexType> mergedClasses) {
+ static Builder createBuilderForFixup(Builder builder, VerticallyMergedClasses mergedClasses) {
Builder newBuilder = new Builder(builder.dexItemFactory);
for (Map.Entry<DexField, DexField> entry : builder.fieldMap.entrySet()) {
newBuilder.map(
@@ -235,7 +231,7 @@
public VerticalClassMergerGraphLens build(
AppView<?> appView, VerticallyMergedClasses mergedClasses) {
- if (mergedClasses.getForwardMap().isEmpty()) {
+ if (mergedClasses.isEmpty()) {
return null;
}
BiMap<DexField, DexField> originalFieldSignatures = fieldMap.inverse();
@@ -254,11 +250,11 @@
}
private DexField getFieldSignatureAfterClassMerging(
- DexField field, Map<DexType, DexType> mergedClasses) {
+ DexField field, VerticallyMergedClasses mergedClasses) {
assert !field.holder.isArrayType();
DexType holder = field.holder;
- DexType newHolder = mergedClasses.getOrDefault(holder, holder);
+ DexType newHolder = mergedClasses.getTargetForOrDefault(holder, holder);
DexType type = field.type;
DexType newType = getTypeAfterClassMerging(type, mergedClasses);
@@ -270,11 +266,11 @@
}
private DexMethod getMethodSignatureAfterClassMerging(
- DexMethod signature, Map<DexType, DexType> mergedClasses) {
+ DexMethod signature, VerticallyMergedClasses mergedClasses) {
assert !signature.holder.isArrayType();
DexType holder = signature.holder;
- DexType newHolder = mergedClasses.getOrDefault(holder, holder);
+ DexType newHolder = mergedClasses.getTargetForOrDefault(holder, holder);
DexProto proto = signature.proto;
DexProto newProto =
@@ -287,16 +283,16 @@
return dexItemFactory.createMethod(newHolder, newProto, signature.name);
}
- private DexType getTypeAfterClassMerging(DexType type, Map<DexType, DexType> mergedClasses) {
+ private DexType getTypeAfterClassMerging(DexType type, VerticallyMergedClasses mergedClasses) {
if (type.isArrayType()) {
DexType baseType = type.toBaseType(dexItemFactory);
- DexType newBaseType = mergedClasses.getOrDefault(baseType, baseType);
+ DexType newBaseType = mergedClasses.getTargetForOrDefault(baseType, baseType);
if (newBaseType != baseType) {
return type.replaceBaseType(newBaseType, dexItemFactory);
}
return type;
}
- return mergedClasses.getOrDefault(type, type);
+ return mergedClasses.getTargetForOrDefault(type, type);
}
public boolean hasMappingForSignatureInContext(DexProgramClass context, DexMethod signature) {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
index 303df06..f5d4b66 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
@@ -65,9 +65,9 @@
return Comparator
// The first item to compare is the synthesizing context type. This is the type used to
// choose the context prefix for items.
- .comparing(SynthesizingContext::getSynthesizingContextType, DexType::slowCompareTo)
+ .comparing(SynthesizingContext::getSynthesizingContextType)
// To ensure that equals coincides with compareTo == 0, we then compare 'type'.
- .thenComparing(c -> c.inputContextType, DexType::slowCompareTo)
+ .thenComparing(c -> c.inputContextType)
.compare(this, other);
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
index 30d232e..01f8825 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.synthesis;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.utils.structural.RepresentativeMap;
import com.google.common.hash.HashCode;
/**
@@ -26,7 +27,7 @@
abstract DexProgramClass getHolder();
- abstract HashCode computeHash(boolean intermediate);
+ abstract HashCode computeHash(RepresentativeMap map, boolean intermediate);
abstract boolean isEquivalentTo(SyntheticDefinition other, boolean intermediate);
}
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 1a96106..4d621a3 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.shaking.MainDexClasses;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.structural.RepresentativeMap;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -93,7 +94,7 @@
MainDexClasses mainDexClasses = appView.appInfo().getMainDexClasses();
GraphLens graphLens = appView.graphLens();
- List<SyntheticMethodDefinition> methodDefinitions =
+ Map<DexType, SyntheticMethodDefinition> methodDefinitions =
lookupSyntheticMethodDefinitions(application);
Collection<List<SyntheticMethodDefinition>> potentialEquivalences =
@@ -183,7 +184,7 @@
// Use a tree set to make sure that we have an ordering on the types.
// These types are put in an array in annotations in the output and we
// need a consistent ordering on them.
- TreeSet<DexType> synthesized = new TreeSet<>(DexType::slowCompareTo);
+ TreeSet<DexType> synthesized = new TreeSet<>(DexType::compareTo);
entry.getValue().stream()
.map(dexProgramClass -> dexProgramClass.type)
.forEach(synthesized::add);
@@ -358,7 +359,7 @@
EquivalenceGroup<T> group = groups.get(i);
// 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 || checkGroupsAreDistict(groups.get(i - 1), group);
+ assert i == 0 || checkGroupsAreDistinct(groups.get(i - 1), group);
DexType representativeType = createExternalType(context, i, factory);
equivalences.put(representativeType, group);
}
@@ -388,7 +389,7 @@
return groups;
}
- private static <T extends SyntheticDefinition & Comparable<T>> boolean checkGroupsAreDistict(
+ private static <T extends SyntheticDefinition & Comparable<T>> boolean checkGroupsAreDistinct(
EquivalenceGroup<T> g1, EquivalenceGroup<T> g2) {
assert g1.compareTo(g2) != 0;
return true;
@@ -417,18 +418,24 @@
}
private static <T extends SyntheticDefinition> Collection<List<T>> computePotentialEquivalences(
- List<T> definitions, boolean intermediate) {
+ Map<DexType, T> definitions, boolean intermediate) {
+ if (definitions.isEmpty()) {
+ return Collections.emptyList();
+ }
+ Set<DexType> allTypes = definitions.keySet();
+ DexType representative = allTypes.iterator().next();
+ RepresentativeMap map = t -> allTypes.contains(t) ? representative : t;
Map<HashCode, List<T>> equivalences = new HashMap<>(definitions.size());
- for (T definition : definitions) {
- HashCode hash = definition.computeHash(intermediate);
+ for (T definition : definitions.values()) {
+ HashCode hash = definition.computeHash(map, intermediate);
equivalences.computeIfAbsent(hash, k -> new ArrayList<>()).add(definition);
}
return equivalences.values();
}
- private List<SyntheticMethodDefinition> lookupSyntheticMethodDefinitions(
+ private Map<DexType, SyntheticMethodDefinition> lookupSyntheticMethodDefinitions(
DexApplication finalApp) {
- List<SyntheticMethodDefinition> methods = new ArrayList<>(syntheticItems.size());
+ Map<DexType, SyntheticMethodDefinition> methods = new IdentityHashMap<>(syntheticItems.size());
for (SyntheticReference reference : syntheticItems.values()) {
SyntheticDefinition definition = reference.lookupDefinition(finalApp::definitionFor);
if (definition == null || !(definition instanceof SyntheticMethodDefinition)) {
@@ -438,7 +445,7 @@
}
SyntheticMethodDefinition method = (SyntheticMethodDefinition) definition;
if (SyntheticMethodBuilder.isValidSyntheticMethod(method.getMethod().getDefinition())) {
- methods.add(method);
+ methods.put(method.getHolder().getType(), method);
} else {
// Failing this check indicates that an optimization has modified the synthetic in a
// disruptive way.
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java
index 8cddcfc..ecc1a81 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.utils.structural.RepresentativeMap;
import com.google.common.hash.HashCode;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
@@ -41,14 +42,14 @@
}
@Override
- HashCode computeHash(boolean intermediate) {
+ HashCode computeHash(RepresentativeMap map, boolean intermediate) {
Hasher hasher = Hashing.sha256().newHasher();
if (intermediate) {
// If in intermediate mode, include the context type as sharing is restricted to within a
// single context.
hasher.putInt(getContext().getSynthesizingContextType().hashCode());
}
- method.getDefinition().hashSyntheticContent(hasher);
+ method.getDefinition().hashSyntheticContent(hasher, map);
return hasher.hash();
}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java
index ce8f565..b11a901 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.tracereferences;
import static com.android.tools.r8.utils.FileUtils.isArchive;
+import static com.android.tools.r8.utils.FileUtils.isClassFile;
import static com.android.tools.r8.utils.FileUtils.isDexFile;
+import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION;
import com.android.tools.r8.ArchiveClassFileProvider;
import com.android.tools.r8.ClassFileResourceProvider;
@@ -25,6 +27,7 @@
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
@@ -33,6 +36,9 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
@Keep
public class TraceReferencesCommand {
@@ -96,6 +102,10 @@
return TraceReferencesCommandParser.parse(args, origin, diagnosticsHandler);
}
+ public static Builder parse(Collection<String> args, Origin origin) {
+ return TraceReferencesCommandParser.parse(args.toArray(new String[args.size()]), origin);
+ }
+
public boolean isPrintHelp() {
return printHelp;
}
@@ -151,6 +161,79 @@
return this;
}
+ private static String extractClassDescriptor(byte[] data) {
+ class ClassNameExtractor extends ClassVisitor {
+ private String className;
+
+ private ClassNameExtractor() {
+ super(ASM_VERSION);
+ }
+
+ @Override
+ public void visit(
+ int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ className = name;
+ }
+
+ String getClassInternalType() {
+ return className;
+ }
+ }
+
+ ClassReader reader = new ClassReader(data);
+ ClassNameExtractor extractor = new ClassNameExtractor();
+ reader.accept(
+ extractor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+ return "L" + extractor.getClassInternalType() + ";";
+ }
+
+ private static class SingleClassClassFileResourceProvider implements ClassFileResourceProvider {
+ private final String descriptor;
+ private final ProgramResource programResource;
+
+ SingleClassClassFileResourceProvider(Origin origin, byte[] data) {
+ this.descriptor = extractClassDescriptor(data);
+ this.programResource =
+ ProgramResource.fromBytes(origin, Kind.CF, data, ImmutableSet.of(descriptor));
+ }
+
+ @Override
+ public Set<String> getClassDescriptors() {
+ return ImmutableSet.of(descriptor);
+ }
+
+ @Override
+ public ProgramResource getProgramResource(String descriptor) {
+ return descriptor.equals(this.descriptor) ? programResource : null;
+ }
+ }
+
+ private ClassFileResourceProvider singleClassFileClassFileResourceProvider(Path file)
+ throws IOException {
+ return new SingleClassClassFileResourceProvider(
+ new PathOrigin(file), Files.readAllBytes(file));
+ }
+
+ private ProgramResourceProvider singleClassFileProgramResourceProvider(Path file)
+ throws IOException {
+ byte[] bytes = Files.readAllBytes(file);
+ String descriptor = extractClassDescriptor(bytes);
+ return new ProgramResourceProvider() {
+
+ @Override
+ public Collection<ProgramResource> getProgramResources() {
+ return ImmutableList.of(
+ ProgramResource.fromBytes(
+ new PathOrigin(file), Kind.CF, bytes, ImmutableSet.of(descriptor)));
+ }
+ };
+ }
+
private void addLibraryOrTargetFile(
Path file, ImmutableList.Builder<ClassFileResourceProvider> builder) {
if (!Files.exists(file)) {
@@ -165,6 +248,12 @@
} catch (IOException e) {
error(new ExceptionDiagnostic(e, new PathOrigin(file)));
}
+ } else if (isClassFile(file)) {
+ try {
+ builder.add(singleClassFileClassFileResourceProvider(file));
+ } catch (IOException e) {
+ error(new ExceptionDiagnostic(e));
+ }
} else {
error(new StringDiagnostic("Unsupported source file type", new PathOrigin(file)));
}
@@ -178,6 +267,12 @@
}
if (isArchive(file)) {
traceSourceBuilder.add(ArchiveResourceProvider.fromArchive(file, false));
+ } else if (isClassFile(file)) {
+ try {
+ traceSourceBuilder.add(singleClassFileProgramResourceProvider(file));
+ } catch (IOException e) {
+ error(new ExceptionDiagnostic(e));
+ }
} else if (isDexFile(file)) {
traceSourceBuilder.add(
new ProgramResourceProvider() {
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java
index f299459..3a3faeb 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java
@@ -6,8 +6,10 @@
import com.android.tools.r8.BaseCompilerCommandParser;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.JdkClassFileProvider;
+import com.android.tools.r8.StringConsumer.FileConsumer;
+import com.android.tools.r8.StringConsumer.WriterConsumer;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.tracereferences.TraceReferencesFormattingConsumer.OutputFormat;
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FlagFile;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -15,6 +17,7 @@
import com.google.common.collect.Iterables;
import java.io.IOException;
import java.io.PrintStream;
+import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -24,29 +27,40 @@
class TraceReferencesCommandParser {
private static final Set<String> OPTIONS_WITH_PARAMETER =
- ImmutableSet.of("--lib", "--target", "--source", "--format", "--output");
+ ImmutableSet.of("--lib", "--target", "--source", "--output");
static final String USAGE_MESSAGE =
String.join(
"\n",
Iterables.concat(
Arrays.asList(
- "Usage: tracereferences [options] [@<argfile>]",
- " Each <argfile> is a file containing additional arguments (one per line)",
+ "Usage: tracereferences <command> [<options>] [@<argfile>]",
+ " Where <command> is one of:",
+ " --check # Run emitting only diagnostics messages.",
+ " --print-usage # Traced references will be output in the print-usage",
+ " # format.",
+ " --keep-rules [<keep-rules-options>]",
+ " # Traced references will be output in the keep-rules",
+ " # format.",
+ " and each <argfile> is a file containing additional options (one per line)",
" and options are:",
" --lib <file|jdk-home> # Add <file|jdk-home> runtime library.",
" --source <file> # Add <file> as a source for tracing references.",
" [--target <file>] # Add <file> as a target for tracing references. When",
" # target is not specified all references from source",
- " # outside of library are treated as a missing"
- + " references.",
- " [--format printuses|keep|keepallowobfuscation]",
- " # Format of the output. Default is 'printuses'.",
- " --output <file> # Output result in <outfile>."),
+ " # outside of library are treated as a missing",
+ " # references.",
+ " --output <file> # Output result in <outfile>. If not passed the",
+ " # result will go to standard out.",
+ " # result will go to standard out."),
BaseCompilerCommandParser.MAP_DIAGNOSTICS_USAGE_MESSAGE,
Arrays.asList(
" --version # Print the version of tracereferences.",
- " --help # Print this message.")));
+ " --help # Print this message.",
+ " and <keep-rule-options> are:",
+ " --allowobfuscation # Output keep rules with the allowobfuscation",
+ " # modifier (defaults to rules without the"
+ + " modifier)")));
/**
* Parse the tracereferences command-line.
*
@@ -76,11 +90,30 @@
.parse(args, origin, TraceReferencesCommand.builder(handler));
}
+ private enum Command {
+ CHECK,
+ PRINTUSAGE,
+ KEEP_RULES;
+ }
+
+ private void checkCommandNotSet(
+ Command command, TraceReferencesCommand.Builder builder, Origin origin) {
+ if (command != null) {
+ builder.error(new StringDiagnostic("Multiple commands specified", origin));
+ }
+ }
+
private TraceReferencesCommand.Builder parse(
String[] args, Origin origin, TraceReferencesCommand.Builder builder) {
String[] expandedArgs = FlagFile.expandFlagFiles(args, builder::error);
Path output = null;
- OutputFormat format = null;
+ Command command = null;
+ boolean allowObfuscation = false;
+ if (expandedArgs.length == 0) {
+ builder.error(new StringDiagnostic("Missing command"));
+ return builder;
+ }
+ // Parse options.
for (int i = 0; i < expandedArgs.length; i++) {
String arg = expandedArgs[i].trim();
String nextArg = null;
@@ -97,32 +130,33 @@
continue;
} else if (arg.equals("--help")) {
builder.setPrintHelp(true);
+ return builder;
} else if (arg.equals("--version")) {
builder.setPrintVersion(true);
+ return builder;
+ } else if (arg.equals("--check")) {
+ checkCommandNotSet(command, builder, origin);
+ command = Command.CHECK;
+ } else if (arg.equals("--print-usage")) {
+ checkCommandNotSet(command, builder, origin);
+ command = Command.PRINTUSAGE;
+ } else if (arg.equals("--keep-rules")) {
+ checkCommandNotSet(command, builder, origin);
+ command = Command.KEEP_RULES;
+ } else if (arg.equals("--allowobfuscation")) {
+ allowObfuscation = true;
} else if (arg.equals("--lib")) {
addLibraryArgument(builder, origin, nextArg);
} else if (arg.equals("--target")) {
builder.addTargetFiles(Paths.get(nextArg));
} else if (arg.equals("--source")) {
builder.addSourceFiles(Paths.get(nextArg));
- } else if (arg.equals("--format")) {
- if (format != null) {
- builder.error(new StringDiagnostic("--format specified multiple times"));
- }
- if (nextArg.equals("printuses")) {
- format = TraceReferencesFormattingConsumer.OutputFormat.PRINTUSAGE;
- }
- if (nextArg.equals("keep")) {
- format = TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES;
- }
- if (nextArg.equals("keepallowobfuscation")) {
- format = TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION;
- }
- if (format == null) {
- builder.error(new StringDiagnostic("Unsupported format '" + nextArg + "'"));
- }
} else if (arg.equals("--output")) {
- output = Paths.get(nextArg);
+ if (output != null) {
+ builder.error(new StringDiagnostic("Option '--output' passed multiple times.", origin));
+ } else {
+ output = Paths.get(nextArg);
+ }
} else if (arg.startsWith("@")) {
builder.error(new StringDiagnostic("Recursive @argfiles are not supported: ", origin));
} else {
@@ -133,28 +167,67 @@
i += argsConsumed;
continue;
}
- builder.error(new StringDiagnostic("Unsupported argument '" + arg + "'"));
+ builder.error(new StringDiagnostic("Unsupported option '" + arg + "'", origin));
}
}
- if (format == null) {
- format = TraceReferencesFormattingConsumer.OutputFormat.PRINTUSAGE;
+
+ if (command == null) {
+ builder.error(
+ new StringDiagnostic(
+ "Missing command, specify one of 'check', '--print-usage' or '--keep-rules'",
+ origin));
+ return builder;
}
- final Path finalOutput = output;
- builder.setConsumer(
- new TraceReferencesFormattingConsumer(format) {
- @Override
- public void finished() {
- PrintStream out = System.out;
- if (finalOutput != null) {
- try {
- out = new PrintStream(Files.newOutputStream(finalOutput));
- } catch (IOException e) {
- builder.error(new ExceptionDiagnostic(e));
+
+ if (command == Command.CHECK && output != null) {
+ builder.error(
+ new StringDiagnostic(
+ "Using '--output' requires command '--print-usage' or '--keep-rules'", origin));
+ return builder;
+ }
+
+ if (command != Command.KEEP_RULES && allowObfuscation) {
+ builder.error(
+ new StringDiagnostic(
+ "Using '--allowobfuscation' requires command '--keep-rules'", origin));
+ return builder;
+ }
+
+ switch (command) {
+ case CHECK:
+ builder.setConsumer(TraceReferencesConsumer.emptyConsumer());
+ break;
+ case KEEP_RULES:
+ builder.setConsumer(
+ TraceReferencesKeepRules.builder()
+ .setAllowObfuscation(allowObfuscation)
+ .setOutputConsumer(
+ output != null
+ ? new FileConsumer(output)
+ : new WriterConsumer(null, new PrintWriter(System.out)))
+ .build());
+ break;
+ case PRINTUSAGE:
+ final Path finalOutput = output;
+ builder.setConsumer(
+ new TraceReferencesPrintUsage() {
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ PrintStream out = System.out;
+ if (finalOutput != null) {
+ try {
+ out = new PrintStream(Files.newOutputStream(finalOutput));
+ } catch (IOException e) {
+ handler.error(new ExceptionDiagnostic(e));
+ }
+ }
+ out.print(get());
}
- }
- out.print(get());
- }
- });
+ });
+ break;
+ default:
+ throw new Unreachable();
+ }
return builder;
}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesConsumer.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesConsumer.java
index 5750fe1..f364c72 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesConsumer.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesConsumer.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.tracereferences;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.Keep;
import com.android.tools.r8.KeepForSubclassing;
import com.android.tools.r8.references.ClassReference;
@@ -71,22 +72,64 @@
@Keep
interface TracedMethod extends TracedReference<MethodReference, MethodAccessFlags> {}
- /** Class has been traced. */
- void acceptType(TracedClass tracedClass);
+ /**
+ * Callback when class has been traced.
+ *
+ * <p>The consumer is expected not to throw, but instead report any errors via the diagnostics
+ * {@param handler}. If an error is reported via {@param handler} and no exceptions are thrown,
+ * then trace references guaranties to exit with an error.
+ *
+ * @param tracedClass Traced class
+ * @param handler Diagnostics handler for reporting.
+ */
+ void acceptType(TracedClass tracedClass, DiagnosticsHandler handler);
- /** Field has been traced. */
- void acceptField(TracedField tracedField);
+ /**
+ * Callback when class has been traced.
+ *
+ * <p>The consumer is expected not to throw, but instead report any errors via the diagnostics
+ * {@param handler}. If an error is reported via {@param handler} and no exceptions are thrown,
+ * then trace references guaranties to exit with an error.
+ *
+ * @param tracedField Traced field
+ * @param handler Diagnostics handler for reporting.
+ */
+ void acceptField(TracedField tracedField, DiagnosticsHandler handler);
- /** Method has been traced. */
- void acceptMethod(TracedMethod tracedMethod);
+ /**
+ * Callback when class has been traced.
+ *
+ * <p>The consumer is expected not to throw, but instead report any errors via the diagnostics
+ * {@param handler}. If an error is reported via {@param handler} and no exceptions are thrown,
+ * then trace references guaranties to exit with an error.
+ *
+ * @param tracedMethod Traced method
+ * @param handler Diagnostics handler for reporting.
+ */
+ void acceptMethod(TracedMethod tracedMethod, DiagnosticsHandler handler);
- /** Package which is required for package private access has been traced. */
- default void acceptPackage(PackageReference pkg) {}
+ /**
+ * Callback when package which is required for package private access has been traced.
+ *
+ * <p>The consumer is expected not to throw, but instead report any errors via the diagnostics
+ * {@param handler}. If an error is reported via {@param handler} and no exceptions are thrown,
+ * then trace references guaranties to exit with an error.
+ *
+ * @param pkg Traced package
+ * @param handler Diagnostics handler for reporting.
+ */
+ default void acceptPackage(PackageReference pkg, DiagnosticsHandler handler) {}
/**
* Tracing has finished. There will be no more calls to any of the <code>acceptXXX</code> methods.
+ *
+ * <p>The consumer is expected not to throw, but instead report any errors via the diagnostics
+ * {@param handler}. If an error is reported via {@param handler} and no exceptions are thrown,
+ * then trace references guaranties to exit with an error.
+ *
+ * @param handler Diagnostics handler for reporting.
*/
- default void finished() {}
+ default void finished(DiagnosticsHandler handler) {}
static TraceReferencesConsumer emptyConsumer() {
return ForwardingConsumer.EMPTY_CONSUMER;
@@ -105,37 +148,37 @@
}
@Override
- public void acceptType(TracedClass tracedClass) {
+ public void acceptType(TracedClass tracedClass, DiagnosticsHandler handler) {
if (consumer != null) {
- consumer.acceptType(tracedClass);
+ consumer.acceptType(tracedClass, handler);
}
}
@Override
- public void acceptField(TracedField tracedField) {
+ public void acceptField(TracedField tracedField, DiagnosticsHandler handler) {
if (consumer != null) {
- consumer.acceptField(tracedField);
+ consumer.acceptField(tracedField, handler);
}
}
@Override
- public void acceptMethod(TracedMethod tracedMethod) {
+ public void acceptMethod(TracedMethod tracedMethod, DiagnosticsHandler handler) {
if (consumer != null) {
- consumer.acceptMethod(tracedMethod);
+ consumer.acceptMethod(tracedMethod, handler);
}
}
@Override
- public void acceptPackage(PackageReference pkg) {
+ public void acceptPackage(PackageReference pkg, DiagnosticsHandler handler) {
if (consumer != null) {
- consumer.acceptPackage(pkg);
+ consumer.acceptPackage(pkg, handler);
}
}
@Override
- public void finished() {
+ public void finished(DiagnosticsHandler handler) {
if (consumer != null) {
- consumer.finished();
+ consumer.finished(handler);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesFormattingConsumer.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesFormattingConsumer.java
deleted file mode 100644
index 67167c4..0000000
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesFormattingConsumer.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.tracereferences;
-
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.references.PackageReference;
-
-class TraceReferencesFormattingConsumer implements TraceReferencesConsumer {
-
- public enum OutputFormat {
- /** Format used with the -printusage flag */
- PRINTUSAGE,
- /** Keep rules keeping each of the traced references */
- KEEP_RULES,
- /**
- * Keep rules with <code>allowobfuscation</code> modifier keeping each of the traced references
- */
- KEEP_RULES_WITH_ALLOWOBFUSCATION
- }
-
- private final OutputFormat format;
- private final TraceReferencesResult.Builder builder = TraceReferencesResult.builder();
- private boolean finishedCalled = false;
-
- public TraceReferencesFormattingConsumer(OutputFormat format) {
- this.format = format;
- }
-
- @Override
- public void acceptType(TracedClass type) {
- assert !finishedCalled;
- builder.acceptType(type);
- }
-
- @Override
- public void acceptField(TracedField field) {
- assert !finishedCalled;
- builder.acceptField(field);
- }
-
- @Override
- public void acceptMethod(TracedMethod method) {
- assert !finishedCalled;
- builder.acceptMethod(method);
- }
-
- @Override
- public void acceptPackage(PackageReference pkg) {
- assert !finishedCalled;
- builder.acceptPackage(pkg);
- }
-
- @Override
- public void finished() {
- assert !finishedCalled;
- finishedCalled = true;
- }
-
- public String get() {
- TraceReferencesResult result = builder.build();
- Formatter formatter;
- switch (format) {
- case PRINTUSAGE:
- formatter = new PrintUsesFormatter();
- break;
- case KEEP_RULES:
- formatter = new KeepRuleFormatter(false);
- break;
- case KEEP_RULES_WITH_ALLOWOBFUSCATION:
- formatter = new KeepRuleFormatter(true);
- break;
- default:
- throw new Unreachable();
- }
- formatter.format(result);
- return formatter.get();
- }
-}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesKeepRules.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesKeepRules.java
new file mode 100644
index 0000000..6940f63
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesKeepRules.java
@@ -0,0 +1,100 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.tracereferences;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.StringConsumer;
+import com.android.tools.r8.StringConsumer.FileConsumer;
+import java.nio.file.Path;
+
+/**
+ * Consumer to format the result of running {@link TraceReferences} as keep rules.
+ *
+ * <p>To build an instance of this consumer, use the {@link TraceReferencesKeepRules.Builder} class.
+ * For example:
+ *
+ * <pre>
+ * TraceReferencesKeepRules consumer = TraceReferencesKeepRules.builder()
+ * .setAllowObfuscation(true)
+ * .setOutputPath(Paths.get("references-to-keep.rules"))
+ * .build();
+ * </pre>
+ */
+@Keep
+public class TraceReferencesKeepRules extends TraceReferencesConsumer.ForwardingConsumer {
+
+ private final TraceReferencesResult.Builder traceReferencesResultBuilder;
+ private final StringConsumer consumer;
+ private final boolean allowObfuscation;
+
+ private TraceReferencesKeepRules(
+ TraceReferencesResult.Builder traceReferencesResultBuilder,
+ StringConsumer consumer,
+ boolean allowObfuscation) {
+ super(traceReferencesResultBuilder);
+ this.traceReferencesResultBuilder = traceReferencesResultBuilder;
+ this.consumer = consumer;
+ this.allowObfuscation = allowObfuscation;
+ }
+
+ /**
+ * Builder for constructing a {@link TraceReferencesKeepRules].
+ *
+ * <p>A builder is obtained by calling {@link TraceReferencesKeepRules#builder}.
+ */
+ @Keep
+ public static class Builder {
+ private StringConsumer consumer;
+ private boolean allowObfuscation;
+
+ /**
+ * Indicate if the generated keep rules should have the <code>allobobfuscation</code> modifier.
+ */
+ public Builder setAllowObfuscation(boolean value) {
+ allowObfuscation = value;
+ return this;
+ }
+
+ /**
+ * Set the output of the keep rules to a file.
+ *
+ * @param output Path to write the output to.
+ */
+ public Builder setOutputPath(Path output) {
+ this.consumer = new FileConsumer(output);
+ return this;
+ }
+
+ /**
+ * Set the output of the keep rules to a {@link com.android.tools.r8.StringConsumer}.
+ *
+ * @param consumer Consumer to send the output to.
+ */
+ public Builder setOutputConsumer(StringConsumer consumer) {
+ this.consumer = consumer;
+ return this;
+ }
+
+ /** Build the {@link TraceReferencesKeepRules} instance. */
+ public TraceReferencesKeepRules build() {
+ return new TraceReferencesKeepRules(
+ TraceReferencesResult.builder(), consumer, allowObfuscation);
+ }
+ }
+
+ /** Create a builder for constructing an instance of {@link TraceReferencesKeepRules.Builder}. */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ super.finished(handler);
+ Formatter formatter = new KeepRuleFormatter(allowObfuscation);
+ formatter.format(traceReferencesResultBuilder.build());
+ consumer.accept(formatter.get(), handler);
+ consumer.finished(handler);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesPrintUsage.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesPrintUsage.java
new file mode 100644
index 0000000..80e8096
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesPrintUsage.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.tracereferences;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.references.PackageReference;
+
+class TraceReferencesPrintUsage implements TraceReferencesConsumer {
+
+ private final TraceReferencesResult.Builder traceReferencesResultBuilder =
+ TraceReferencesResult.builder();
+ private boolean finishedCalled = false;
+
+ @Override
+ public void acceptType(TracedClass type, DiagnosticsHandler handler) {
+ assert !finishedCalled;
+ traceReferencesResultBuilder.acceptType(type, handler);
+ }
+
+ @Override
+ public void acceptField(TracedField field, DiagnosticsHandler handler) {
+ assert !finishedCalled;
+ traceReferencesResultBuilder.acceptField(field, handler);
+ }
+
+ @Override
+ public void acceptMethod(TracedMethod method, DiagnosticsHandler handler) {
+ assert !finishedCalled;
+ traceReferencesResultBuilder.acceptMethod(method, handler);
+ }
+
+ @Override
+ public void acceptPackage(PackageReference pkg, DiagnosticsHandler handler) {
+ assert !finishedCalled;
+ traceReferencesResultBuilder.acceptPackage(pkg, handler);
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ assert !finishedCalled;
+ finishedCalled = true;
+ }
+
+ public String get() {
+ Formatter formatter = new PrintUsesFormatter();
+ formatter.format(traceReferencesResultBuilder.build());
+ return formatter.get();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesResult.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesResult.java
index f8dab44..e40f3d7 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesResult.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesResult.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.tracereferences;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
@@ -48,7 +49,7 @@
private final Set<PackageReference> keepPackageNames = new HashSet<>();
@Override
- public void acceptType(TracedClass tracedClass) {
+ public void acceptType(TracedClass tracedClass, DiagnosticsHandler handler) {
types.add(tracedClass);
if (tracedClass.isMissingDefinition()) {
this.missingDefinition.add(tracedClass.getReference());
@@ -56,7 +57,7 @@
}
@Override
- public void acceptField(TracedField tracedField) {
+ public void acceptField(TracedField tracedField, DiagnosticsHandler handler) {
FieldReference field = tracedField.getReference();
fields.computeIfAbsent(field.getHolderClass(), k -> new HashSet<>()).add(tracedField);
if (tracedField.isMissingDefinition()) {
@@ -65,7 +66,7 @@
}
@Override
- public void acceptMethod(TracedMethod tracedMethod) {
+ public void acceptMethod(TracedMethod tracedMethod, DiagnosticsHandler handler) {
MethodReference method = tracedMethod.getReference();
methods.computeIfAbsent(method.getHolderClass(), k -> new HashSet<>()).add(tracedMethod);
if (tracedMethod.isMissingDefinition()) {
@@ -74,12 +75,12 @@
}
@Override
- public void acceptPackage(PackageReference pkg) {
+ public void acceptPackage(PackageReference pkg, DiagnosticsHandler handler) {
keepPackageNames.add(pkg);
}
@Override
- public void finished() {}
+ public void finished(DiagnosticsHandler handler) {}
TraceReferencesResult build() {
missingDefinition.forEach(
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
index 72db701..8b571a8 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
@@ -249,7 +249,7 @@
clazz.forEachProgramMethod(useCollector::registerMethod);
clazz.forEachField(useCollector::registerField);
}
- consumer.finished();
+ consumer.finished(diagnostics);
useCollector.reportMissingDefinitions();
}
@@ -287,10 +287,11 @@
TracedClassImpl tracedClass = new TracedClassImpl(type, clazz);
checkMissingDefinition(tracedClass);
if (isTargetType(type) || tracedClass.isMissingDefinition()) {
- consumer.acceptType(tracedClass);
+ consumer.acceptType(tracedClass, diagnostics);
if (!tracedClass.isMissingDefinition()
&& clazz.accessFlags.isVisibilityDependingOnPackage()) {
- consumer.acceptPackage(Reference.packageFromString(clazz.type.getPackageName()));
+ consumer.acceptPackage(
+ Reference.packageFromString(clazz.type.getPackageName()), diagnostics);
}
}
}
@@ -305,10 +306,11 @@
TracedFieldImpl tracedField = new TracedFieldImpl(field, baseField);
checkMissingDefinition(tracedField);
if (isTargetType(field.holder) || tracedField.isMissingDefinition()) {
- consumer.acceptField(tracedField);
+ consumer.acceptField(tracedField, diagnostics);
if (!tracedField.isMissingDefinition()
&& baseField.accessFlags.isVisibilityDependingOnPackage()) {
- consumer.acceptPackage(Reference.packageFromString(baseField.holder().getPackageName()));
+ consumer.acceptPackage(
+ Reference.packageFromString(baseField.holder().getPackageName()), diagnostics);
}
}
}
@@ -323,11 +325,12 @@
DexEncodedMethod definition = method.lookupOnClass(holder);
TracedMethodImpl tracedMethod = new TracedMethodImpl(method, definition);
if (isTargetType(method.holder) || tracedMethod.isMissingDefinition()) {
- consumer.acceptMethod(tracedMethod);
+ consumer.acceptMethod(tracedMethod, diagnostics);
checkMissingDefinition(tracedMethod);
if (!tracedMethod.isMissingDefinition()
&& definition.accessFlags.isVisibilityDependingOnPackage()) {
- consumer.acceptPackage(Reference.packageFromString(definition.holder().getPackageName()));
+ consumer.acceptPackage(
+ Reference.packageFromString(definition.holder().getPackageName()), diagnostics);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
index f15c049..6e501fc 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
@@ -4,14 +4,12 @@
package com.android.tools.r8.utils;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.structural.Ordered;
import java.util.Arrays;
-import java.util.Comparator;
import java.util.List;
-/**
- * Android API level description
- */
-public enum AndroidApiLevel {
+/** Android API level description */
+public enum AndroidApiLevel implements Ordered<AndroidApiLevel> {
B(1),
B_1_1(2),
C(3),
@@ -70,9 +68,7 @@
}
public static List<AndroidApiLevel> getAndroidApiLevelsSorted() {
- List<AndroidApiLevel> androidApiLevels = Arrays.asList(AndroidApiLevel.values());
- androidApiLevels.sort(Comparator.comparingInt(AndroidApiLevel::getLevel));
- return androidApiLevels;
+ return Arrays.asList(AndroidApiLevel.values());
}
public static AndroidApiLevel getMinAndroidApiLevel(DexVersion dexVersion) {
@@ -157,20 +153,4 @@
return LATEST;
}
}
-
- public boolean isLessThan(AndroidApiLevel other) {
- return this.level < other.getLevel();
- }
-
- public boolean isLessThanOrEqualTo(AndroidApiLevel other) {
- return this.level <= other.getLevel();
- }
-
- public boolean isGreaterThan(AndroidApiLevel other) {
- return other.isLessThan(this);
- }
-
- public boolean isGreaterThanOrEqualTo(AndroidApiLevel other) {
- return other.isLessThanOrEqualTo(this);
- }
}
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 c426a09..4478209 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -20,6 +20,7 @@
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;
@@ -81,13 +82,16 @@
*/
public class AndroidApp {
+ // TODO(b/172887664): Move to DumpOptions and capitalize.
private static final String dumpVersionFileName = "r8-version";
private static final String dumpBuildPropertiesFileName = "build.properties";
private static final String dumpDesugaredLibraryFileName = "desugared-library.json";
+ private static final String dumpMainDexListResourceFileName = "main-dex-list.txt";
private static final String dumpProgramFileName = "program.jar";
private static final String dumpClasspathFileName = "classpath.jar";
private static final String dumpLibraryFileName = "library.jar";
private static final String dumpConfigFileName = "proguard.config";
+ private static final String dumpInputConfigFileName = "proguard_input.config";
private static Map<FeatureSplit, String> dumpFeatureSplitFileNames(
FeatureSplitConfiguration featureSplitConfiguration) {
@@ -454,7 +458,10 @@
return programResourcesMainDescriptor.get(resource);
}
- public void dump(Path output, InternalOptions options) {
+ public void dump(Path output, DumpOptions options, Reporter reporter, DexItemFactory factory) {
+ if (options == null) {
+ return;
+ }
int nextDexIndex = 0;
OpenOption[] openOptions =
new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
@@ -462,50 +469,60 @@
writeToZipStream(
out, dumpVersionFileName, Version.getVersionString().getBytes(), ZipEntry.DEFLATED);
writeToZipStream(
- out,
- dumpBuildPropertiesFileName,
- getBuildPropertiesContents(options).getBytes(),
- ZipEntry.DEFLATED);
- if (options.desugaredLibraryConfiguration.getJsonSource() != null) {
+ out, dumpBuildPropertiesFileName, options.dumpOptions().getBytes(), ZipEntry.DEFLATED);
+ if (options.getDesugaredLibraryJsonSource() != null) {
writeToZipStream(
out,
dumpDesugaredLibraryFileName,
- options.desugaredLibraryConfiguration.getJsonSource().getBytes(),
+ options.getDesugaredLibraryJsonSource().getBytes(),
ZipEntry.DEFLATED);
- if (options.dumpInputToFile != null) {
- options.reporter.warning(
+ if (options.dumpInputToFile()) {
+ reporter.warning(
"Dumping a compilation with desugared library on a file may prevent reproduction,"
+ " use dumpInputToDirectory property instead.");
}
}
- if (options.getProguardConfiguration() != null) {
- String proguardConfig = options.getProguardConfiguration().getParsedConfiguration();
+ if (options.getParsedProguardConfiguration() != null) {
+ String proguardConfig = options.getParsedProguardConfiguration();
writeToZipStream(out, dumpConfigFileName, proguardConfig.getBytes(), ZipEntry.DEFLATED);
}
+ if (proguardMapInputData != null) {
+ reporter.warning(
+ "Dumping proguard map input data may have side effects due to I/O on Paths.");
+ writeToZipStream(
+ out,
+ dumpInputConfigFileName,
+ proguardMapInputData.getString().getBytes(),
+ ZipEntry.DEFLATED);
+ }
+ if (hasMainDexList()) {
+ List<String> mainDexList = new ArrayList<>();
+ if (hasMainDexListResources()) {
+ reporter.warning(
+ "Dumping main dex list resources may have side effects due to I/O on Paths.");
+ for (StringResource mainDexListResource : getMainDexListResources()) {
+ mainDexList.add(mainDexListResource.getString());
+ }
+ }
+ mainDexList.addAll(getMainDexClasses());
+ String join = StringUtils.join(mainDexList, "\n");
+ writeToZipStream(out, dumpMainDexListResourceFileName, join.getBytes(), ZipEntry.DEFLATED);
+ }
nextDexIndex =
dumpProgramResources(
dumpProgramFileName,
- dumpFeatureSplitFileNames(options.featureSplitConfiguration),
+ options.getFeatureSplitConfiguration(),
nextDexIndex,
out,
- options);
+ reporter,
+ factory);
nextDexIndex = dumpClasspathResources(nextDexIndex, out);
nextDexIndex = dumpLibraryResources(nextDexIndex, out);
} catch (IOException | ResourceException e) {
- throw options.reporter.fatalError(new ExceptionDiagnostic(e));
+ throw reporter.fatalError(new ExceptionDiagnostic(e));
}
}
- private String getBuildPropertiesContents(InternalOptions options) {
- return String.join(
- "\n",
- ImmutableList.of(
- "mode=" + (options.debug ? "debug" : "release"),
- "min-api=" + options.minApiLevel,
- "tree-shaking=" + options.isTreeShakingEnabled(),
- "minification=" + options.isMinificationEnabled()));
- }
-
private int dumpLibraryResources(int nextDexIndex, ZipOutputStream out)
throws IOException, ResourceException {
nextDexIndex =
@@ -547,19 +564,22 @@
private int dumpProgramResources(
String archiveName,
- Map<FeatureSplit, String> featureSplitArchiveNames,
+ FeatureSplitConfiguration featureSplitConfiguration,
int nextDexIndex,
ZipOutputStream out,
- InternalOptions options)
+ Reporter reporter,
+ DexItemFactory dexItemFactory)
throws IOException, ResourceException {
+
+ Map<FeatureSplit, String> featureSplitArchiveNames =
+ dumpFeatureSplitFileNames(featureSplitConfiguration);
Map<FeatureSplit, ByteArrayOutputStream> featureSplitArchiveByteStreams =
new IdentityHashMap<>();
Map<FeatureSplit, ZipOutputStream> featureSplitArchiveOutputStreams = new IdentityHashMap<>();
try {
- DexItemFactory dexItemFactory = options.dexItemFactory();
- FeatureSplitConfiguration featureSplitConfiguration = options.featureSplitConfiguration;
ClassToFeatureSplitMap classToFeatureSplitMap =
- ClassToFeatureSplitMap.createInitialClassToFeatureSplitMap(options);
+ ClassToFeatureSplitMap.createInitialClassToFeatureSplitMap(
+ dexItemFactory, featureSplitConfiguration, reporter);
if (featureSplitConfiguration != null) {
for (FeatureSplit featureSplit : featureSplitConfiguration.getFeatureSplits()) {
ByteArrayOutputStream archiveByteStream = new ByteArrayOutputStream();
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 c74ed38..9116bb5 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -11,6 +11,7 @@
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.ProgramConsumer;
import com.android.tools.r8.StringConsumer;
@@ -40,6 +41,7 @@
import com.android.tools.r8.graph.EnumValueInfoMapCollection;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
+import com.android.tools.r8.graph.classmerging.StaticallyMergedClasses;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.inspector.internal.InspectorImpl;
@@ -227,6 +229,7 @@
public boolean enableNeverMergePrefixes = true;
public Set<String> neverMergePrefixes = ImmutableSet.of("j$.");
+ public boolean classpathInterfacesMayHaveStaticInitialization = false;
public boolean libraryInterfacesMayHaveStaticInitialization = false;
// Optimization-related flags. These should conform to -dontoptimize and disableAllOptimizations.
@@ -235,6 +238,7 @@
System.getProperty("com.android.tools.r8.fieldBitAccessAnalysis") != null;
public boolean enableStaticClassMerging = true;
public boolean enableHorizontalClassMerging = true;
+ public boolean enableHorizontalClassMergingOfKotlinLambdas = true;
public boolean enableVerticalClassMerging = true;
public boolean enableArgumentRemoval = true;
public boolean enableUnusedInterfaceRemoval = true;
@@ -333,6 +337,9 @@
// Boolean value indicating that byte code pass through may be enabled.
public boolean enableCfByteCodePassThrough = false;
+ // Contain the contents of the build properties file from the compiler command.
+ public DumpOptions dumpOptions;
+
// Hidden marker for classes.dex
private boolean hasMarker = false;
private Marker marker;
@@ -1176,6 +1183,8 @@
// b/172508621
public boolean sortMethodsOnCfOutput =
System.getProperty("com.android.tools.r8.sortMethodsOnCfWriting") != null;
+ public boolean allowDesugaredInput =
+ System.getProperty("com.android.tools.r8.allowDesugaredInput") != null;
}
public static class CallSiteOptimizationOptions {
@@ -1274,6 +1283,9 @@
public BiConsumer<DexItemFactory, HorizontallyMergedLambdaClasses>
horizontallyMergedLambdaClassesConsumer = ConsumerUtils.emptyBiConsumer();
+ public BiConsumer<DexItemFactory, StaticallyMergedClasses> staticallyMergedClassesConsumer =
+ ConsumerUtils.emptyBiConsumer();
+
public BiConsumer<DexItemFactory, EnumValueInfoMapCollection> unboxedEnumsConsumer =
ConsumerUtils.emptyBiConsumer();
@@ -1338,6 +1350,8 @@
public boolean forceIRForCfToCfDesugar =
System.getProperty("com.android.tools.r8.forceIRForCfToCfDesugar") != null;
public boolean disableMappingToOriginalProgramVerification = false;
+ public boolean allowInvalidCfAccessFlags =
+ System.getProperty("com.android.tools.r8.allowInvalidCfAccessFlags") != null;
// Flag to allow processing of resources in D8. A data resource consumer still needs to be
// specified.
@@ -1425,7 +1439,7 @@
public boolean canUseConstClassInstructions(CfVersion cfVersion) {
assert isGeneratingClassFiles();
- return cfVersion.isGreaterThanOrEqual(requiredCfVersionForConstClassInstructions());
+ return cfVersion.isGreaterThanOrEqualTo(requiredCfVersionForConstClassInstructions());
}
public CfVersion requiredCfVersionForConstClassInstructions() {
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index f2358c5..51a3778 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -308,7 +308,7 @@
// Then process the methods, ordered by renamed name.
List<DexString> renamedMethodNames = new ArrayList<>(methodsByRenamedName.keySet());
- renamedMethodNames.sort(DexString::slowCompareTo);
+ renamedMethodNames.sort(DexString::compareTo);
for (DexString methodName : renamedMethodNames) {
List<DexEncodedMethod> methods = methodsByRenamedName.get(methodName);
if (methods.size() > 1) {
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index 51821c0..d3c376f 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -7,6 +7,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -47,13 +48,12 @@
return result;
}
- public static <T> boolean removeFirstMatch(List<T> list, Predicate<T> element) {
+ public static <T> Optional<T> removeFirstMatch(List<T> list, Predicate<T> element) {
int index = firstIndexMatching(list, element);
if (index >= 0) {
- list.remove(index);
- return true;
+ return Optional.of(list.remove(index));
}
- return false;
+ return Optional.empty();
}
public static <T extends Comparable<T>> boolean verifyListIsOrdered(List<T> list) {
diff --git a/src/main/java/com/android/tools/r8/utils/ObjectUtils.java b/src/main/java/com/android/tools/r8/utils/ObjectUtils.java
index 1f3ad9d..216f19b 100644
--- a/src/main/java/com/android/tools/r8/utils/ObjectUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ObjectUtils.java
@@ -22,4 +22,14 @@
}
return null;
}
+
+ /**
+ * If the object is null return the default value, otherwise compute the function with the value.
+ */
+ public static <S, T> T mapNotNullOrDefault(S object, T def, Function<? super S, ? extends T> fn) {
+ if (object != null) {
+ return fn.apply(object);
+ }
+ return def;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/ZipUtils.java b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
index f33ed6f..400ef0c 100644
--- a/src/main/java/com/android/tools/r8/utils/ZipUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -23,6 +23,7 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -68,7 +69,11 @@
}
public static void iter(String zipFileStr, OnEntryHandler handler) throws IOException {
- try (ZipFile zipFile = new ZipFile(zipFileStr, StandardCharsets.UTF_8)) {
+ iter(Paths.get(zipFileStr), handler);
+ }
+
+ public static void iter(Path zipFilePath, OnEntryHandler handler) throws IOException {
+ try (ZipFile zipFile = new ZipFile(zipFilePath.toFile(), StandardCharsets.UTF_8)) {
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
@@ -79,6 +84,12 @@
}
}
+ public static byte[] readSingleEntry(Path zipFilePath, String name) throws IOException {
+ try (ZipFile zipFile = new ZipFile(zipFilePath.toFile(), StandardCharsets.UTF_8)) {
+ return ByteStreams.toByteArray(zipFile.getInputStream(zipFile.getEntry(name)));
+ }
+ }
+
public static void zip(Path zipFile, Path inputDirectory) throws IOException {
List<Path> files =
Files.walk(inputDirectory)
@@ -226,4 +237,8 @@
return zipFile;
}
}
+
+ public static String zipEntryNameForClass(Class<?> clazz) {
+ return DescriptorUtils.getClassBinaryName(clazz) + CLASS_EXTENSION;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java
index 7410339..361b761 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.utils.collections;
-import java.util.Collection;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.LinkedHashSet;
@@ -14,13 +13,38 @@
public class BidirectionalManyToOneMap<K, V> {
- private final Map<K, V> backing = new IdentityHashMap<>();
- private final Map<V, Set<K>> inverse = new IdentityHashMap<>();
+ private final Map<K, V> backing;
+ private final Map<V, Set<K>> inverse;
+
+ public BidirectionalManyToOneMap() {
+ this(new IdentityHashMap<>(), new IdentityHashMap<>());
+ }
+
+ private BidirectionalManyToOneMap(Map<K, V> backing, Map<V, Set<K>> inverse) {
+ this.backing = backing;
+ this.inverse = inverse;
+ }
+
+ public static <K, V> BidirectionalManyToOneMap<K, V> empty() {
+ return new BidirectionalManyToOneMap<>(Collections.emptyMap(), Collections.emptyMap());
+ }
+
+ public boolean containsKey(K key) {
+ return backing.containsKey(key);
+ }
+
+ public boolean containsValue(V value) {
+ return inverse.containsKey(value);
+ }
public void forEach(BiConsumer<Set<K>, V> consumer) {
inverse.forEach((value, keys) -> consumer.accept(keys, value));
}
+ public V get(K key) {
+ return backing.get(key);
+ }
+
public V getOrDefault(K key, V value) {
return backing.getOrDefault(key, value);
}
@@ -64,13 +88,25 @@
}
}
+ public Set<K> removeValue(V value) {
+ Set<K> keys = inverse.remove(value);
+ if (keys == null) {
+ return Collections.emptySet();
+ }
+ for (K key : keys) {
+ V removedValue = backing.remove(key);
+ assert removedValue == value;
+ }
+ return keys;
+ }
+
public void put(K key, V value) {
remove(key);
backing.put(key, value);
inverse.computeIfAbsent(value, ignore -> new LinkedHashSet<>()).add(key);
}
- public Collection<V> values() {
- return backing.values();
+ public Set<V> values() {
+ return inverse.keySet();
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java
index 2412cfd..f058bf1 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java
@@ -53,6 +53,12 @@
return result;
}
+ public static ProgramMethodSet create(ProgramMethodSet methodSet) {
+ ProgramMethodSet newMethodSet = create();
+ newMethodSet.addAll(methodSet);
+ return newMethodSet;
+ }
+
public static ProgramMethodSet createConcurrent() {
return new ProgramMethodSet(ConcurrentHashMap::new);
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/SortedProgramMethodSet.java b/src/main/java/com/android/tools/r8/utils/collections/SortedProgramMethodSet.java
index d902126..d625aae 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/SortedProgramMethodSet.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/SortedProgramMethodSet.java
@@ -41,13 +41,13 @@
public static SortedProgramMethodSet create(ForEachable<ProgramMethod> methods) {
SortedProgramMethodSet result =
- new SortedProgramMethodSet(() -> new TreeMap<>(DexMethod::slowCompareTo));
+ new SortedProgramMethodSet(() -> new TreeMap<>(DexMethod::compareTo));
methods.forEach(result::add);
return result;
}
public static SortedProgramMethodSet createConcurrent() {
- return new SortedProgramMethodSet(() -> new ConcurrentSkipListMap<>(DexMethod::slowCompareTo));
+ return new SortedProgramMethodSet(() -> new ConcurrentSkipListMap<>(DexMethod::compareTo));
}
public static SortedProgramMethodSet empty() {
@@ -64,7 +64,7 @@
@Override
public Set<DexEncodedMethod> toDefinitionSet() {
Comparator<DexEncodedMethod> comparator =
- (x, y) -> x.getReference().slowCompareTo(y.getReference());
+ (x, y) -> x.getReference().compareTo(y.getReference());
Set<DexEncodedMethod> definitions = new TreeSet<>(comparator);
forEach(method -> definitions.add(method.getDefinition()));
return definitions;
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitor.java
new file mode 100644
index 0000000..832d172
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitor.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils.structural;
+
+import com.android.tools.r8.graph.DexField;
+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.graph.DexTypeList;
+import java.util.Comparator;
+
+/** Base class for a visitor implementing compareTo on a structural item. */
+public abstract class CompareToVisitor {
+
+ public abstract void visitBool(boolean value1, boolean value2);
+
+ public abstract void visitInt(int value1, int value2);
+
+ public abstract void visitDexString(
+ DexString string1, DexString string2, Comparator<DexString> comparator);
+
+ public abstract void visitDexType(DexType type1, DexType type2);
+
+ public abstract void visitDexTypeList(DexTypeList types1, DexTypeList types2);
+
+ public void visitDexField(DexField field1, DexField field2) {
+ visit(field1, field2, field1.getStructuralAccept());
+ }
+
+ public void visitDexMethod(DexMethod method1, DexMethod method2) {
+ visit(method1, method2, method1.getStructuralAccept());
+ }
+
+ public abstract <S> void visit(S item1, S item2, StructuralAccept<S> accept);
+
+ @Deprecated
+ public abstract <S> void visit(S item1, S item2, Comparator<S> comparator);
+}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java
new file mode 100644
index 0000000..b7d016a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java
@@ -0,0 +1,132 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils.structural;
+
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.utils.structural.StructuralItem.CompareToAccept;
+import com.android.tools.r8.utils.structural.StructuralItem.HashingAccept;
+import java.util.Comparator;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.ToIntFunction;
+
+/** Base class to share most visiting methods */
+public abstract class CompareToVisitorBase extends CompareToVisitor {
+
+ private int order = 0;
+
+ public final boolean stillEqual() {
+ return order == 0;
+ }
+
+ public final int getOrder() {
+ return order;
+ }
+
+ public final void setOrder(int order) {
+ this.order = order;
+ }
+
+ @Override
+ public final void visitBool(boolean value1, boolean value2) {
+ if (stillEqual()) {
+ setOrder(Boolean.compare(value1, value2));
+ }
+ }
+
+ @Override
+ public final void visitInt(int value1, int value2) {
+ if (stillEqual()) {
+ setOrder(Integer.compare(value1, value2));
+ }
+ }
+
+ @Override
+ public final void visitDexString(
+ DexString string1, DexString string2, Comparator<DexString> comparator) {
+ if (stillEqual()) {
+ setOrder(comparator.compare(string1, string2));
+ }
+ }
+
+ @Override
+ public final void visitDexTypeList(DexTypeList types1, DexTypeList types2) {
+ // Comparison is lexicographic with comparisons between items prior to the length of the lists.
+ if (stillEqual()) {
+ int length = Math.min(types1.size(), types2.size());
+ for (int i = 0; i < length && stillEqual(); i++) {
+ visitDexType(types1.values[i], types2.values[i]);
+ }
+ if (stillEqual()) {
+ visitInt(types1.size(), types2.size());
+ }
+ }
+ }
+
+ @Override
+ public final <S> void visit(S item1, S item2, Comparator<S> comparator) {
+ if (stillEqual()) {
+ setOrder(comparator.compare(item1, item2));
+ }
+ }
+
+ @Override
+ public final <S> void visit(S item1, S item2, StructuralAccept<S> accept) {
+ if (stillEqual()) {
+ accept.accept(new ItemSpecification<>(item1, item2, this));
+ }
+ }
+
+ private static class ItemSpecification<T>
+ extends StructuralSpecification<T, ItemSpecification<T>> {
+
+ private final CompareToVisitorBase parent;
+ private final T item1;
+ private final T item2;
+
+ private ItemSpecification(T item1, T item2, CompareToVisitorBase parent) {
+ this.item1 = item1;
+ this.item2 = item2;
+ this.parent = parent;
+ }
+
+ @Override
+ public ItemSpecification<T> withAssert(Predicate<T> predicate) {
+ assert predicate.test(item1);
+ assert predicate.test(item2);
+ return this;
+ }
+
+ @Override
+ public ItemSpecification<T> withBool(Predicate<T> getter) {
+ parent.visitBool(getter.test(item1), getter.test(item2));
+ return this;
+ }
+
+ @Override
+ public ItemSpecification<T> withInt(ToIntFunction<T> getter) {
+ parent.visitInt(getter.applyAsInt(item1), getter.applyAsInt(item2));
+ return this;
+ }
+
+ @Override
+ public <S> ItemSpecification<T> withConditionalCustomItem(
+ Predicate<T> predicate,
+ Function<T, S> getter,
+ CompareToAccept<S> compare,
+ HashingAccept<S> hasher) {
+ if (parent.stillEqual()) {
+ boolean test1 = predicate.test(item1);
+ boolean test2 = predicate.test(item2);
+ if (test1 && test2) {
+ compare.accept(getter.apply(item1), getter.apply(item2), parent);
+ } else {
+ parent.visitBool(test1, test2);
+ }
+ }
+ return this;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithNamingLens.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithNamingLens.java
new file mode 100644
index 0000000..a16575d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithNamingLens.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils.structural;
+
+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.naming.NamingLens;
+import com.android.tools.r8.utils.structural.StructuralItem.CompareToAccept;
+
+public class CompareToVisitorWithNamingLens extends CompareToVisitorBase {
+
+ public static <T> int run(T item1, T item2, NamingLens namingLens, StructuralAccept<T> visit) {
+ return run(item1, item2, namingLens, (i1, i2, visitor) -> visitor.visit(i1, i2, visit));
+ }
+
+ public static <T> int run(
+ T item1, T item2, NamingLens namingLens, CompareToAccept<T> compareToAccept) {
+ CompareToVisitorWithNamingLens state = new CompareToVisitorWithNamingLens(namingLens);
+ compareToAccept.accept(item1, item2, state);
+ return state.getOrder();
+ }
+
+ private final NamingLens namingLens;
+
+ public CompareToVisitorWithNamingLens(NamingLens namingLens) {
+ this.namingLens = namingLens;
+ }
+
+ @Override
+ public void visitDexType(DexType type1, DexType type2) {
+ if (stillEqual()) {
+ namingLens.lookupDescriptor(type1).acceptCompareTo(namingLens.lookupDescriptor(type2), this);
+ }
+ }
+
+ @Override
+ public void visitDexField(DexField field1, DexField field2) {
+ if (stillEqual()) {
+ field1.holder.acceptCompareTo(field2.holder, this);
+ if (stillEqual()) {
+ namingLens.lookupName(field1).acceptCompareTo(namingLens.lookupName(field2), this);
+ if (stillEqual()) {
+ field1.type.acceptCompareTo(field2.type, this);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void visitDexMethod(DexMethod method1, DexMethod method2) {
+ if (stillEqual()) {
+ method1.holder.acceptCompareTo(method2.holder, this);
+ if (stillEqual()) {
+ namingLens.lookupName(method1).acceptCompareTo(namingLens.lookupName(method2), this);
+ if (stillEqual()) {
+ method1.proto.acceptCompareTo(method2.proto, this);
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeEquivalence.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeEquivalence.java
new file mode 100644
index 0000000..f77ab53
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeEquivalence.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils.structural;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.structural.StructuralItem.CompareToAccept;
+
+public class CompareToVisitorWithTypeEquivalence extends CompareToVisitorBase {
+
+ public static <T> int run(T item1, T item2, RepresentativeMap map, StructuralAccept<T> visit) {
+ return run(item1, item2, map, (i1, i2, visitor) -> visitor.visit(i1, i2, visit));
+ }
+
+ public static <T> int run(
+ T item1, T item2, RepresentativeMap map, CompareToAccept<T> compareToAccept) {
+ CompareToVisitorWithTypeEquivalence state = new CompareToVisitorWithTypeEquivalence(map);
+ compareToAccept.accept(item1, item2, state);
+ return state.getOrder();
+ }
+
+ private final RepresentativeMap representatives;
+
+ public CompareToVisitorWithTypeEquivalence(RepresentativeMap representatives) {
+ this.representatives = representatives;
+ }
+
+ @Override
+ public void visitDexType(DexType type1, DexType type2) {
+ if (stillEqual()) {
+ DexType repr1 = representatives.getRepresentative(type1);
+ DexType repr2 = representatives.getRepresentative(type2);
+ repr1.getDescriptor().acceptCompareTo(repr2.getDescriptor(), this);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/DefaultCompareToVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/DefaultCompareToVisitor.java
new file mode 100644
index 0000000..9f91d19
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/structural/DefaultCompareToVisitor.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils.structural;
+
+import com.android.tools.r8.utils.structural.StructuralItem.CompareToAccept;
+
+/**
+ * Default implementation for defining compareTo on a structural type.
+ *
+ * <p>Internally this is using CompareToVisitorWithTypeEquivalence with the identity map, but should
+ * not be assumed to have that implementation.
+ */
+public class DefaultCompareToVisitor {
+
+ public static <T> int run(T item1, T item2, StructuralAccept<T> visit) {
+ return run(item1, item2, (i1, i2, visitor) -> visitor.visit(i1, i2, visit));
+ }
+
+ public static <T> int run(T item1, T item2, CompareToAccept<T> compareToAccept) {
+ return CompareToVisitorWithTypeEquivalence.run(item1, item2, t -> t, compareToAccept);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/DefaultHashingVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/DefaultHashingVisitor.java
new file mode 100644
index 0000000..2ec503c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/structural/DefaultHashingVisitor.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils.structural;
+
+import com.android.tools.r8.utils.structural.StructuralItem.HashingAccept;
+import com.google.common.hash.Hasher;
+
+/**
+ * Default visitor for hashing a structural item.
+ *
+ * <p>Internally this is using HashingVisitorWithTypeEquivalence with the identity map, but should
+ * not be assumed to have that implementation.
+ */
+public class DefaultHashingVisitor {
+
+ public static <T> void run(T item, Hasher hasher, StructuralAccept<T> accept) {
+ run(item, hasher, (i, visitor) -> visitor.visit(i, accept));
+ }
+
+ public static <T> void run(T item, Hasher hasher, HashingAccept<T> hashingAccept) {
+ HashingVisitorWithTypeEquivalence.run(item, hasher, t -> t, hashingAccept);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/Equatable.java b/src/main/java/com/android/tools/r8/utils/structural/Equatable.java
new file mode 100644
index 0000000..be49958
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/structural/Equatable.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils.structural;
+
+public interface Equatable<T> {
+
+ /**
+ * Typed definition of equality.
+ *
+ * <p>Subclasses must implement this and override Object.equals(Object) with equalsImpl.
+ */
+ boolean isEqualTo(T other);
+
+ /**
+ * An equatable type must define an equality compatible hashing.
+ *
+ * <p>Note: that that the declaration here will not enforce an implementation in the concrete
+ * class and it cannot be defined by a default method. Implementors of Equatable must ensure to
+ * override it.
+ */
+ @Override
+ int hashCode();
+
+ /**
+ * An equatable type must override Object.equals by the intended implementation below.
+ *
+ * <p>Note: that that the declaration here will not enforce an implementation in the concrete
+ * class and it cannot be defined by a default method. Implementors of Equatable must ensure to
+ * override it.
+ */
+ @Override
+ boolean equals(Object other);
+
+ /**
+ * Implementation for Object.equals(Object).
+ *
+ * <p>It is not possible to define default methods on java.lang.Object, thus concrete subclasses
+ * must manually override equals as:
+ *
+ * <pre>
+ * @Override boolean equals(Object other) { return Equatable.equalsImpl(this, other); }
+ * </pre>
+ */
+ @SuppressWarnings("unchecked")
+ static <T extends Equatable<T>> boolean equalsImpl(T self, Object other) {
+ assert self != null;
+ if (self == other) {
+ return true;
+ }
+ if (other == null || self.getClass() != other.getClass()) {
+ return false;
+ }
+ return self.isEqualTo((T) other);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java
new file mode 100644
index 0000000..ab75fb4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils.structural;
+
+import com.android.tools.r8.utils.structural.StructuralItem.CompareToAccept;
+import com.android.tools.r8.utils.structural.StructuralItem.HashingAccept;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.ToIntFunction;
+
+/**
+ * Simple hash code implementation.
+ *
+ * <p>This visitor relies on the specification of hashCode on all object types. Thus it does not
+ * have a call-back structure that requires the spec implementation as well as a visitor for the
+ * recursive decent. There is also no support for overriding the visitation apart from the usual
+ * override of hashCode().
+ */
+public class HashCodeVisitor<T> extends StructuralSpecification<T, HashCodeVisitor<T>> {
+
+ public static <T> int run(T item, StructuralAccept<T> visit) {
+ HashCodeVisitor<T> visitor = new HashCodeVisitor<>(item);
+ visit.accept(visitor);
+ return visitor.hashCode;
+ }
+
+ private final T item;
+
+ private int hashCode = 0;
+
+ private HashCodeVisitor(T item) {
+ this.item = item;
+ }
+
+ private HashCodeVisitor<T> amend(int value) {
+ // This mirrors the behavior of Objects.hash(values...) / Arrays.hashCode(array).
+ hashCode = 31 * hashCode + value;
+ return this;
+ }
+
+ @Override
+ public HashCodeVisitor<T> withAssert(Predicate<T> predicate) {
+ assert predicate.test(item);
+ return this;
+ }
+
+ @Override
+ public HashCodeVisitor<T> withBool(Predicate<T> getter) {
+ return amend(Boolean.hashCode(getter.test(item)));
+ }
+
+ @Override
+ public HashCodeVisitor<T> withInt(ToIntFunction<T> getter) {
+ return amend(Integer.hashCode(getter.applyAsInt(item)));
+ }
+
+ @Override
+ protected <S> HashCodeVisitor<T> withConditionalCustomItem(
+ Predicate<T> predicate,
+ Function<T, S> getter,
+ CompareToAccept<S> compare,
+ HashingAccept<S> hasher) {
+ if (predicate.test(item)) {
+ return amend(getter.apply(item).hashCode());
+ } else {
+ // Use the value 1 for the failing-predicate case such that a different hash is obtained for
+ // eg, {null, null} and {null}.
+ return amend(1);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitor.java
new file mode 100644
index 0000000..bfd5e0d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitor.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils.structural;
+
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.google.common.hash.Hasher;
+import java.util.function.BiConsumer;
+
+public abstract class HashingVisitor {
+
+ public abstract void visitBool(boolean value);
+
+ public abstract void visitInt(int value);
+
+ public abstract void visitDexString(DexString string);
+
+ public abstract void visitDexType(DexType type);
+
+ public abstract void visitDexTypeList(DexTypeList types);
+
+ public abstract <S> void visit(S item, StructuralAccept<S> accept);
+
+ @Deprecated
+ public abstract <S> void visit(S item, BiConsumer<S, Hasher> hasher);
+}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
new file mode 100644
index 0000000..2036b1b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils.structural;
+
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.utils.structural.StructuralItem.CompareToAccept;
+import com.android.tools.r8.utils.structural.StructuralItem.HashingAccept;
+import com.google.common.hash.Hasher;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.ToIntFunction;
+
+/** Visitor for hashing a structural item under some assumed type equivalence. */
+public class HashingVisitorWithTypeEquivalence extends HashingVisitor {
+
+ public static <T> void run(
+ T item, Hasher hasher, RepresentativeMap map, StructuralAccept<T> accept) {
+ run(item, hasher, map, (i, visitor) -> visitor.visit(i, accept));
+ }
+
+ public static <T> void run(
+ T item, Hasher hasher, RepresentativeMap map, HashingAccept<T> hashingAccept) {
+ hashingAccept.accept(item, new HashingVisitorWithTypeEquivalence(hasher, map));
+ }
+
+ private final Hasher hash;
+ private final RepresentativeMap representatives;
+
+ private HashingVisitorWithTypeEquivalence(Hasher hash, RepresentativeMap representatives) {
+ this.hash = hash;
+ this.representatives = representatives;
+ }
+
+ @Override
+ public void visitBool(boolean value) {
+ hash.putBoolean(value);
+ }
+
+ @Override
+ public void visitInt(int value) {
+ hash.putInt(value);
+ }
+
+ @Override
+ public void visitDexString(DexString string) {
+ visitInt(string.hashCode());
+ }
+
+ @Override
+ public void visitDexType(DexType type) {
+ visitDexString(representatives.getRepresentative(type).getDescriptor());
+ }
+
+ @Override
+ public void visitDexTypeList(DexTypeList types) {
+ types.forEach(this::visitDexType);
+ }
+
+ @Override
+ public <S> void visit(S item, StructuralAccept<S> accept) {
+ accept.accept(new ItemSpecification<>(item, this));
+ }
+
+ @Override
+ public <S> void visit(S item, BiConsumer<S, Hasher> hasher) {
+ hasher.accept(item, hash);
+ }
+
+ private static class ItemSpecification<T>
+ extends StructuralSpecification<T, ItemSpecification<T>> {
+
+ private final HashingVisitorWithTypeEquivalence parent;
+ private final T item;
+
+ private ItemSpecification(T item, HashingVisitorWithTypeEquivalence parent) {
+ this.item = item;
+ this.parent = parent;
+ }
+
+ @Override
+ public ItemSpecification<T> withAssert(Predicate<T> predicate) {
+ assert predicate.test(item);
+ return this;
+ }
+
+ @Override
+ public ItemSpecification<T> withBool(Predicate<T> getter) {
+ parent.visitBool(getter.test(item));
+ return this;
+ }
+
+ @Override
+ public ItemSpecification<T> withInt(ToIntFunction<T> getter) {
+ parent.visitInt(getter.applyAsInt(item));
+ return this;
+ }
+
+ @Override
+ protected <S> ItemSpecification<T> withConditionalCustomItem(
+ Predicate<T> predicate,
+ Function<T, S> getter,
+ CompareToAccept<S> compare,
+ HashingAccept<S> hasher) {
+ boolean test = predicate.test(item);
+ // Always hash the predicate result to distinguish, eg, {null, null} and {null}.
+ parent.visitBool(test);
+ if (test) {
+ hasher.accept(getter.apply(item), parent);
+ }
+ return this;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/Ordered.java b/src/main/java/com/android/tools/r8/utils/structural/Ordered.java
new file mode 100644
index 0000000..9c9a588
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/structural/Ordered.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils.structural;
+
+/** An ordered type is a type with a total order. */
+public interface Ordered<T> extends Equatable<T>, Comparable<T> {
+
+ /** Definition of total order. */
+ @Override
+ int compareTo(T other);
+
+ /** Default equality is now defined by the order. */
+ @Override
+ default boolean isEqualTo(T other) {
+ assert other != null;
+ return this == other || compareTo(other) == 0;
+ }
+
+ static <T extends Ordered<T>> T min(T o1, T o2) {
+ return o1.isLessThan(o2) ? o1 : o2;
+ }
+
+ static <T extends Ordered<T>> T max(T o1, T o2) {
+ return o1.isLessThan(o2) ? o2 : o1;
+ }
+
+ static <T extends Ordered<T>> T minIgnoreNull(T o1, T o2) {
+ if (o1 == null) {
+ return o2;
+ }
+ if (o2 == null) {
+ return o1;
+ }
+ return min(o1, o2);
+ }
+
+ static <T extends Ordered<T>> T maxIgnoreNull(T o1, T o2) {
+ if (o1 == null) {
+ return o2;
+ }
+ if (o2 == null) {
+ return o1;
+ }
+ return o1.isLessThan(o2) ? o2 : o1;
+ }
+
+ default boolean isLessThan(T other) {
+ return compareTo(other) < 0;
+ }
+
+ default boolean isLessThanOrEqualTo(T other) {
+ return compareTo(other) <= 0;
+ }
+
+ default boolean isGreaterThan(T other) {
+ return compareTo(other) > 0;
+ }
+
+ default boolean isGreaterThanOrEqualTo(T other) {
+ return compareTo(other) >= 0;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/RepresentativeMap.java b/src/main/java/com/android/tools/r8/utils/structural/RepresentativeMap.java
new file mode 100644
index 0000000..6a0af38
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/structural/RepresentativeMap.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils.structural;
+
+import com.android.tools.r8.graph.DexType;
+
+@FunctionalInterface
+public interface RepresentativeMap {
+ DexType getRepresentative(DexType type);
+}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/StructuralAccept.java b/src/main/java/com/android/tools/r8/utils/structural/StructuralAccept.java
new file mode 100644
index 0000000..1c6d25c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/structural/StructuralAccept.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils.structural;
+
+@FunctionalInterface
+public interface StructuralAccept<T> {
+ void accept(StructuralSpecification<T, ?> visitor);
+}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/StructuralItem.java b/src/main/java/com/android/tools/r8/utils/structural/StructuralItem.java
new file mode 100644
index 0000000..f077c25
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/structural/StructuralItem.java
@@ -0,0 +1,88 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils.structural;
+
+import com.google.common.hash.Hasher;
+import com.google.common.hash.Hashing;
+
+/** Specified types must implement methods to determine equality, hashing and order. */
+public interface StructuralItem<T extends StructuralItem<T>> extends Ordered<T> {
+
+ T self();
+
+ StructuralAccept<T> getStructuralAccept();
+
+ // CompareTo implementation and callbacks.
+
+ @FunctionalInterface
+ interface CompareToAccept<T> {
+ void accept(T item1, T item2, CompareToVisitor visitor);
+ }
+
+ /**
+ * Implementation of the default compareTo on the item.
+ *
+ * <p>This should *not* be overwritten, instead items should overwrite acceptCompareTo which will
+ * ensure that the effect is in place for any CompareToVisitor.
+ */
+ @Override
+ default int compareTo(T other) {
+ return DefaultCompareToVisitor.run(self(), other, StructuralItem::acceptCompareTo);
+ }
+
+ /**
+ * Implementation of a compareTo with a type equivalence on an item.
+ *
+ * <p>This should *not* be overwritten, instead items should overwrite acceptCompareTo which will
+ * ensure that the effect is in place for any CompareToVisitor.
+ */
+ default int compareWithTypeEquivalenceTo(T other, RepresentativeMap map) {
+ return CompareToVisitorWithTypeEquivalence.run(
+ self(), other, map, StructuralItem::acceptCompareTo);
+ }
+
+ /** Default accept for compareTo visitors. Override to change behavior. */
+ default void acceptCompareTo(T other, CompareToVisitor visitor) {
+ visitor.visit(self(), other, self().getStructuralAccept());
+ }
+
+ // Hashing implemenation and callbacks.
+
+ @FunctionalInterface
+ interface HashingAccept<T> {
+ void accept(T item, HashingVisitor visitor);
+ }
+
+ /**
+ * Implementation of the default hashing of an item.
+ *
+ * <p>This should *not* be overwritten, instead items should overwrite acceptHashing which will
+ * ensure that the effect is in place for any HashingVisitor.
+ */
+ default void hash(Hasher hasher) {
+ DefaultHashingVisitor.run(self(), hasher, self().getStructuralAccept());
+ }
+
+ /** Hashing method to use from tests to avoid having guava types shared between R8 and tests. */
+ default String hashForTesting() {
+ Hasher hasher = Hashing.sha256().newHasher();
+ hash(hasher);
+ return hasher.hash().toString();
+ }
+
+ /**
+ * Implementation of the default hashing with a type equivalence on the item.
+ *
+ * <p>This should *not* be overwritten, instead items should overwrite acceptHashing which will
+ * ensure that the effect is in place for any HashingVisitor.
+ */
+ default void hashWithTypeEquivalence(Hasher hasher, RepresentativeMap map) {
+ HashingVisitorWithTypeEquivalence.run(self(), hasher, map, self().getStructuralAccept());
+ }
+
+ /** Default accept for hashing visitors. Override to change behavior. */
+ default void acceptHashing(HashingVisitor visitor) {
+ visitor.visit(self(), self().getStructuralAccept());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java b/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java
new file mode 100644
index 0000000..bb8f7c7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils.structural;
+
+import com.android.tools.r8.utils.structural.StructuralItem.CompareToAccept;
+import com.android.tools.r8.utils.structural.StructuralItem.HashingAccept;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.ToIntFunction;
+
+public abstract class StructuralSpecification<T, V extends StructuralSpecification<T, V>> {
+
+ /**
+ * Basic specification for visiting an item.
+ *
+ * <p>This specified the getter for the item as well as all of the methods that are required for
+ * visiting. Those coincide with the requirements of Specified.
+ *
+ * <p>It is preferable to use withStructuralItem.
+ */
+ @Deprecated
+ public final <S> V withCustomItem(
+ Function<T, S> getter, CompareToAccept<S> compare, HashingAccept<S> hasher) {
+ return withConditionalCustomItem(t -> true, getter, compare, hasher);
+ }
+
+ protected abstract <S> V withConditionalCustomItem(
+ Predicate<T> predicate,
+ Function<T, S> getter,
+ CompareToAccept<S> compare,
+ HashingAccept<S> hasher);
+
+ /**
+ * Specification for a "specified" item.
+ *
+ * <p>Using this the visiting methods are could based on the implementation of the Specified
+ * interface.
+ */
+ public final <S extends StructuralItem<S>> V withItem(Function<T, S> getter) {
+ return withConditionalItem(t -> true, getter);
+ }
+
+ final <S extends StructuralItem<S>> V withNullableItem(Function<T, S> getter) {
+ return withConditionalItem(s -> getter.apply(s) != null, getter);
+ }
+
+ public final <S extends StructuralItem<S>> V withConditionalItem(
+ Predicate<T> predicate, Function<T, S> getter) {
+ return withConditionalCustomItem(predicate, getter, S::acceptCompareTo, S::acceptHashing);
+ }
+
+ /**
+ * Helper to declare an assert on the item.
+ *
+ * <p>Only run if running with -ea. Must be run on any item being visited (ie, both in the case of
+ * comparisons and equality).
+ */
+ public abstract V withAssert(Predicate<T> predicate);
+
+ // Primitive Java types. These will need overriding to avoid boxing.
+ public abstract V withBool(Predicate<T> getter);
+
+ public abstract V withInt(ToIntFunction<T> getter);
+}
diff --git a/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java b/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java
index 441be7c..8b7d21e 100644
--- a/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java
+++ b/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java
@@ -8,6 +8,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.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
@@ -42,6 +43,7 @@
@Test
public void test() throws Exception {
InternalOptions options = new InternalOptions();
+ options.dumpOptions = DumpOptions.builder(Tool.D8).build();
String dataResourceName = "my-resource.bin";
byte[] dataResourceData = new byte[] {1, 2, 3};
@@ -67,7 +69,7 @@
.build();
Path dumpFile = temp.newFolder().toPath().resolve("dump.zip");
- appIn.dump(dumpFile, options);
+ appIn.dump(dumpFile, options.dumpOptions, options.reporter, options.dexItemFactory());
AndroidApp appOut = AndroidApp.builder(options.reporter).addDump(dumpFile).build();
assertEquals(1, appOut.getClassProgramResourcesForTesting().size());
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 9167acd..058c2e5 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -428,6 +428,44 @@
addInternalKeepRules(sb.toString());
}
+ public T noClassInlining() {
+ return noClassInlining(true);
+ }
+
+ public T noClassInlining(boolean condition) {
+ if (condition) {
+ return addOptionsModification(options -> options.enableClassInlining = false);
+ }
+ return self();
+ }
+
+ public T noClassStaticizing() {
+ return noClassStaticizing(true);
+ }
+
+ public T noClassStaticizing(boolean condition) {
+ if (condition) {
+ return addOptionsModification(options -> options.enableClassStaticizer = false);
+ }
+ return self();
+ }
+
+ public T noHorizontalClassMerging() {
+ return noHorizontalClassMerging(true);
+ }
+
+ public T noHorizontalClassMerging(boolean condition) {
+ if (condition) {
+ return addKeepRules("-" + NoHorizontalClassMergingRule.RULE_NAME + " class *");
+ }
+ return self();
+ }
+
+ public T noHorizontalClassMerging(Class<?> clazz) {
+ return addKeepRules(
+ "-" + NoHorizontalClassMergingRule.RULE_NAME + " class " + clazz.getTypeName());
+ }
+
public T enableNoUnusedInterfaceRemovalAnnotations() {
if (!enableNoUnusedInterfaceRemovalAnnotations) {
enableNoUnusedInterfaceRemovalAnnotations = true;
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 3d24da4..abdfb83 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.utils.codeinspector.EnumUnboxingInspector;
import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import com.android.tools.r8.utils.codeinspector.HorizontallyMergedLambdaClassesInspector;
+import com.android.tools.r8.utils.codeinspector.StaticallyMergedClassesInspector;
import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
import com.google.common.base.Suppliers;
import java.io.ByteArrayOutputStream;
@@ -147,6 +148,17 @@
dexItemFactory, horizontallyMergedLambdaClasses))));
}
+ public T addStaticallyMergedClassesInspector(
+ Consumer<StaticallyMergedClassesInspector> inspector) {
+ return addOptionsModification(
+ options ->
+ options.testing.staticallyMergedClassesConsumer =
+ ((dexItemFactory, staticallyMergedClasses) ->
+ inspector.accept(
+ new StaticallyMergedClassesInspector(
+ dexItemFactory, staticallyMergedClasses))));
+ }
+
public T addVerticallyMergedClassesInspector(
Consumer<VerticallyMergedClassesInspector> inspector) {
return addOptionsModification(
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 5f674e0..6f6c949 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -251,6 +251,10 @@
return self();
}
+ public T addPrintSeeds() {
+ return addKeepRules("-printseeds");
+ }
+
public T allowAccessModification() {
return allowAccessModification(true);
}
@@ -275,6 +279,10 @@
return addKeepAttributes(ProguardKeepAttributes.LINE_NUMBER_TABLE);
}
+ public T addKeepAttributeSignature() {
+ return addKeepAttributes(ProguardKeepAttributes.SIGNATURE);
+ }
+
public T addKeepAttributeSourceFile() {
return addKeepAttributes(ProguardKeepAttributes.SOURCE_FILE);
}
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java
index c9f7d21..81c5c04 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java
@@ -72,6 +72,7 @@
.transform())
.addKeepMainRule(TestClass.class)
.enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/testclasses/BridgeHoistingAccessibilityTestClasses.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/testclasses/BridgeHoistingAccessibilityTestClasses.java
index 9990f62..e7d430f 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/testclasses/BridgeHoistingAccessibilityTestClasses.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/testclasses/BridgeHoistingAccessibilityTestClasses.java
@@ -5,12 +5,14 @@
package com.android.tools.r8.bridgeremoval.hoisting.testclasses;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.bridgeremoval.hoisting.BridgeHoistingAccessibilityTest;
import com.android.tools.r8.bridgeremoval.hoisting.BridgeHoistingAccessibilityTest.CWithRangedInvoke;
public class BridgeHoistingAccessibilityTestClasses {
+ @NoHorizontalClassMerging
@NoVerticalClassMerging
public static class A {
diff --git a/src/test/java/com/android/tools/r8/cf/CfVersionTest.java b/src/test/java/com/android/tools/r8/cf/CfVersionTest.java
index 48871e8..ea38851 100644
--- a/src/test/java/com/android/tools/r8/cf/CfVersionTest.java
+++ b/src/test/java/com/android/tools/r8/cf/CfVersionTest.java
@@ -49,16 +49,16 @@
}
private static void assertLessThan(CfVersion less, CfVersion more) {
- assertFalse(less.isEqual(more));
+ assertFalse(less.isEqualTo(more));
assertEquals(-1, less.compareTo(more));
assertEquals(1, more.compareTo(less));
assertTrue(less.isLessThan(more));
- assertTrue(less.isLessThanOrEqual(more));
+ assertTrue(less.isLessThanOrEqualTo(more));
assertFalse(less.isGreaterThan(more));
- assertFalse(less.isGreaterThanOrEqual(more));
+ assertFalse(less.isGreaterThanOrEqualTo(more));
assertFalse(more.isLessThan(less));
- assertFalse(more.isLessThanOrEqual(less));
+ assertFalse(more.isLessThanOrEqualTo(less));
assertTrue(more.isGreaterThan(less));
- assertTrue(more.isGreaterThanOrEqual(less));
+ assertTrue(more.isGreaterThanOrEqualTo(less));
}
}
diff --git a/src/test/java/com/android/tools/r8/cf/PrintSeedsWithDeserializeLambdaMethodTest.java b/src/test/java/com/android/tools/r8/cf/PrintSeedsWithDeserializeLambdaMethodTest.java
new file mode 100644
index 0000000..497a1bf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/PrintSeedsWithDeserializeLambdaMethodTest.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.cf;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class PrintSeedsWithDeserializeLambdaMethodTest extends TestBase {
+
+ private static final Class<?> TEST_CLASS_CF = KeepDeserializeLambdaMethodTestCf.class;
+ private static final Class<?> TEST_CLASS_DEX = KeepDeserializeLambdaMethodTestDex.class;
+
+ private static final String EXPECTED =
+ StringUtils.lines("base lambda", KeepDeserializeLambdaMethodTest.LAMBDA_MESSAGE);
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection params() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private final TestParameters parameters;
+
+ public PrintSeedsWithDeserializeLambdaMethodTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private Class<?> getMainClass() {
+ return parameters.isCfRuntime() ? TEST_CLASS_CF : TEST_CLASS_DEX;
+ }
+
+ private List<Class<?>> getClasses() {
+ return ImmutableList.of(KeepDeserializeLambdaMethodTest.class, getMainClass());
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8Compat(parameters.getBackend())
+ .addProgramClasses(getClasses())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(getMainClass())
+ .addPrintSeeds()
+ .allowStdoutMessages()
+ .noMinification()
+ .noTreeShaking()
+ .run(parameters.getRuntime(), getMainClass())
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkPresenceOfDeserializedLambdas);
+ }
+
+ private void checkPresenceOfDeserializedLambdas(CodeInspector inspector) {
+ for (Class<?> clazz : getClasses()) {
+ MethodSubject method = inspector.clazz(clazz).uniqueMethodWithName("$deserializeLambda$");
+ assertEquals(
+ "Unexpected status for $deserializedLambda$ on " + clazz.getSimpleName(),
+ parameters.isCfRuntime(),
+ method.isPresent());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingNonTrivialTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingNonTrivialTest.java
new file mode 100644
index 0000000..4d7d7fe
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingNonTrivialTest.java
@@ -0,0 +1,126 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging.horizontal;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class AbstractMethodMergingNonTrivialTest extends HorizontalClassMergingTestBase {
+
+ public AbstractMethodMergingNonTrivialTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(B.class, A.class))
+ .enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("ASub1.f()", "B.f()", "C.f()", "A.g()", "BSub1.g()", "C.g()");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ (System.currentTimeMillis() > 0 ? new ASub1() : new ASub2()).f();
+ (System.currentTimeMillis() > 0 ? new BSub1() : new BSub2()).f();
+ (System.currentTimeMillis() > 0 ? new CSub1() : new CSub2()).f();
+ (System.currentTimeMillis() > 0 ? new ASub1() : new ASub2()).g();
+ (System.currentTimeMillis() > 0 ? new BSub1() : new BSub2()).g();
+ (System.currentTimeMillis() > 0 ? new CSub1() : new CSub2()).g();
+ }
+ }
+
+ @NoVerticalClassMerging
+ abstract static class A {
+
+ public abstract void f();
+
+ @NeverInline
+ public void g() {
+ System.out.println("A.g()");
+ }
+ }
+
+ @NoVerticalClassMerging
+ abstract static class B {
+
+ @NeverInline
+ public void f() {
+ System.out.println("B.f()");
+ }
+
+ public abstract void g();
+ }
+
+ abstract static class C {
+
+ @NeverInline
+ public void f() {
+ System.out.println("C.f()");
+ }
+
+ @NeverInline
+ public void g() {
+ System.out.println("C.g()");
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class ASub1 extends A {
+
+ @Override
+ public void f() {
+ System.out.println("ASub1.f()");
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class ASub2 extends A {
+
+ @Override
+ public void f() {
+ System.out.println("ASub2.f()");
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class BSub1 extends B {
+
+ @Override
+ public void g() {
+ System.out.println("BSub1.g()");
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class BSub2 extends B {
+
+ @Override
+ public void g() {
+ System.out.println("BSub2.g()");
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class CSub1 extends C {}
+
+ @NoHorizontalClassMerging
+ static class CSub2 extends C {}
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingTest.java
new file mode 100644
index 0000000..cc092e9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingTest.java
@@ -0,0 +1,132 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging.horizontal;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class AbstractMethodMergingTest extends HorizontalClassMergingTestBase {
+
+ public AbstractMethodMergingTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(B.class, A.class))
+ .enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(
+ "ASub1.f()", "B.f()", "A.g()", "BSub1.g()", "ASub1.h()", "BSub1.h()");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ (System.currentTimeMillis() > 0 ? new ASub1() : new ASub2()).f();
+ (System.currentTimeMillis() > 0 ? new BSub1() : new BSub2()).f();
+ (System.currentTimeMillis() > 0 ? new ASub1() : new ASub2()).g();
+ (System.currentTimeMillis() > 0 ? new BSub1() : new BSub2()).g();
+ (System.currentTimeMillis() > 0 ? new ASub1() : new ASub2()).h();
+ (System.currentTimeMillis() > 0 ? new BSub1() : new BSub2()).h();
+ }
+ }
+
+ @NoVerticalClassMerging
+ abstract static class A {
+
+ public abstract void f();
+
+ @NeverInline
+ public void g() {
+ System.out.println("A.g()");
+ }
+
+ public abstract void h();
+ }
+
+ @NoVerticalClassMerging
+ abstract static class B {
+
+ @NeverInline
+ public void f() {
+ System.out.println("B.f()");
+ }
+
+ public abstract void g();
+
+ public abstract void h();
+ }
+
+ @NoHorizontalClassMerging
+ static class ASub1 extends A {
+
+ @Override
+ public void f() {
+ System.out.println("ASub1.f()");
+ }
+
+ @Override
+ public void h() {
+ System.out.println("ASub1.h()");
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class ASub2 extends A {
+
+ @Override
+ public void f() {
+ System.out.println("ASub2.f()");
+ }
+
+ @Override
+ public void h() {
+ System.out.println("ASub2.h()");
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class BSub1 extends B {
+
+ @Override
+ public void g() {
+ System.out.println("BSub1.g()");
+ }
+
+ @Override
+ public void h() {
+ System.out.println("BSub1.h()");
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class BSub2 extends B {
+
+ @Override
+ public void g() {
+ System.out.println("BSub2.g()");
+ }
+
+ @Override
+ public void h() {
+ System.out.println("BSub2.h()");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassWithInstanceFieldsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassWithInstanceFieldsTest.java
new file mode 100644
index 0000000..25bb4ec
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassWithInstanceFieldsTest.java
@@ -0,0 +1,86 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class ClassWithInstanceFieldsTest extends HorizontalClassMergingTestBase {
+ public ClassWithInstanceFieldsTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(B.class, A.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A. field: 5, v: a, j: 1", "B. field: b, v: 2, j: 3")
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(
+ codeInspector.clazz(B.class), notIf(isPresent(), enableHorizontalClassMerging));
+ });
+ }
+
+ @NeverClassInline
+ public static class A {
+ public int field;
+ public String v;
+ public int j;
+
+ public A(int field, String v, int j) {
+ this.field = field;
+ this.v = v;
+ this.j = j;
+ }
+
+ @NeverInline
+ public void foo() {
+ System.out.println("A. field: " + field + ", v: " + v + ", j: " + j);
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ public String field;
+ public int v;
+ public int j;
+
+ public B(String field, int v, int j) {
+ this.field = field;
+ this.v = v;
+ this.j = j;
+ }
+
+ @NeverInline
+ public void foo() {
+ System.out.println("B. field: " + field + ", v: " + v + ", j: " + j);
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ new A(5, "a", 1).foo();
+ new B("b", 2, 3).foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentVisibilityFieldsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentVisibilityFieldsTest.java
new file mode 100644
index 0000000..90e407e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentVisibilityFieldsTest.java
@@ -0,0 +1,151 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static com.android.tools.r8.utils.codeinspector.Matchers.readsInstanceField;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.classmerging.horizontal.EmptyClassTest.A;
+import com.android.tools.r8.classmerging.horizontal.EmptyClassTest.B;
+import com.android.tools.r8.classmerging.horizontal.EmptyClassTest.Main;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+
+public class ClassesWithDifferentVisibilityFieldsTest extends HorizontalClassMergingTestBase {
+ public ClassesWithDifferentVisibilityFieldsTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(B.class, A.class))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(
+ "a. v1: 10, v2: 20", "b. v1: 60, v2: 100", "c. v1: 210, v2: 330")
+ .inspect(
+ codeInspector -> {
+ ClassSubject aClassSubject = codeInspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ assertThat(
+ codeInspector.clazz(B.class), notIf(isPresent(), enableHorizontalClassMerging));
+ assertThat(codeInspector.clazz(C.class), isPresent());
+
+ if (enableHorizontalClassMerging) {
+ FieldSubject v1Subject = aClassSubject.uniqueFieldWithName("v1");
+ FieldSubject v2Subject = aClassSubject.uniqueFieldWithName("v2");
+
+ MethodSubject methodSubject = aClassSubject.uniqueMethodWithName("getAV1");
+ assertThat(methodSubject, isPresent());
+ assertThat(methodSubject, readsInstanceField(v1Subject.getDexField()));
+
+ methodSubject = aClassSubject.uniqueMethodWithName("getAV2");
+ assertThat(methodSubject, isPresent());
+ assertThat(methodSubject, readsInstanceField(v2Subject.getDexField()));
+
+ // The fields v1 and v2 are swapped, because their access modifiers are swapped.
+ methodSubject = aClassSubject.uniqueMethodWithName("getBV1");
+ assertThat(methodSubject, isPresent());
+ assertThat(methodSubject, readsInstanceField(v2Subject.getDexField()));
+
+ methodSubject = aClassSubject.uniqueMethodWithName("getBV2");
+ assertThat(methodSubject, isPresent());
+ assertThat(methodSubject, readsInstanceField(v1Subject.getDexField()));
+ }
+ });
+ }
+
+ @NeverClassInline
+ public static class A {
+ private int v1;
+ public int v2;
+
+ public A(int v) {
+ v1 = v;
+ v2 = 2 * v;
+ }
+
+ @NeverInline
+ public int getAV1() {
+ return v1;
+ }
+
+ @NeverInline
+ public int getAV2() {
+ return v2;
+ }
+
+ @NeverInline
+ public void foo() {
+ System.out.println("a. v1: " + getAV1() + ", v2: " + getAV2());
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ public int v1;
+ private int v2;
+
+ public B(int v) {
+ v1 = 3 * v;
+ v2 = 5 * v;
+ }
+
+ @NeverInline
+ public int getBV1() {
+ return v1;
+ }
+
+ @NeverInline
+ public int getBV2() {
+ return v2;
+ }
+
+ @NeverInline
+ public void foo() {
+ System.out.println("b. v1: " + getBV1() + ", v2: " + getBV2());
+ }
+ }
+
+ @NeverClassInline
+ public static class C {
+ public int v1;
+ public int v2;
+
+ public C(int v) {
+ v1 = 7 * v;
+ v2 = 11 * v;
+ }
+
+ @NeverInline
+ public void foo() {
+ System.out.println("c. v1: " + v1 + ", v2: " + v2);
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ new A(10).foo();
+ new B(20).foo();
+ new C(30).foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
index 456d29e..5c03d3b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
@@ -29,8 +29,8 @@
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
- .addHorizontallyMergedClassesInspector(
- inspector -> inspector.assertMergedInto(Z.class, Y.class))
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(Z.class, Y.class))
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("bar", "foo y", "foo z")
.inspect(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
index a30149f..f658909 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
@@ -34,7 +34,8 @@
.addKeepMainRule(Main.class)
.addOptionsModification(
options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
- .addHorizontallyMergedClassesInspector(
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging,
inspector ->
inspector.assertMerged(A.class, B.class).assertMergedIntoDifferentType(B.class))
.enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/IdenticalFieldMembersTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/IdenticalFieldMembersTest.java
index 1cb2d2d..6968e1a 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/IdenticalFieldMembersTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/IdenticalFieldMembersTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.classmerging.horizontal;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.*;
@@ -30,16 +31,9 @@
.assertSuccessWithOutputLines("foo A", "bar 2")
.inspect(
codeInspector -> {
- if (enableHorizontalClassMerging) {
- assertThat(codeInspector.clazz(A.class), isPresent());
- assertThat(codeInspector.clazz(B.class), isPresent());
- // TODO(b/163311975): A and B should be merged
- // assertThat(codeInspector.clazz(B.class), not(isPresent()));
- // TODO(b/165517236): Explicitly check classes have been merged.
- } else {
- assertThat(codeInspector.clazz(A.class), isPresent());
- assertThat(codeInspector.clazz(B.class), isPresent());
- }
+ assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(
+ codeInspector.clazz(B.class), notIf(isPresent(), enableHorizontalClassMerging));
});
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoAbstractClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoAbstractClassesWithNonAbstractClassesTest.java
similarity index 91%
rename from src/test/java/com/android/tools/r8/classmerging/horizontal/NoAbstractClassesTest.java
rename to src/test/java/com/android/tools/r8/classmerging/horizontal/NoAbstractClassesWithNonAbstractClassesTest.java
index 039a65b..2bc2c97 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoAbstractClassesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoAbstractClassesWithNonAbstractClassesTest.java
@@ -14,8 +14,9 @@
import com.android.tools.r8.TestParameters;
import org.junit.Test;
-public class NoAbstractClassesTest extends HorizontalClassMergingTestBase {
- public NoAbstractClassesTest(TestParameters parameters, boolean enableHorizontalClassMerging) {
+public class NoAbstractClassesWithNonAbstractClassesTest extends HorizontalClassMergingTestBase {
+ public NoAbstractClassesWithNonAbstractClassesTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
super(parameters, enableHorizontalClassMerging);
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoAbstractClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapFieldTest.java
similarity index 62%
copy from src/test/java/com/android/tools/r8/classmerging/horizontal/NoAbstractClassesTest.java
copy to src/test/java/com/android/tools/r8/classmerging/horizontal/RemapFieldTest.java
index 039a65b..33c8a85 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoAbstractClassesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapFieldTest.java
@@ -10,12 +10,11 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestParameters;
import org.junit.Test;
-public class NoAbstractClassesTest extends HorizontalClassMergingTestBase {
- public NoAbstractClassesTest(TestParameters parameters, boolean enableHorizontalClassMerging) {
+public class RemapFieldTest extends HorizontalClassMergingTestBase {
+ public RemapFieldTest(TestParameters parameters, boolean enableHorizontalClassMerging) {
super(parameters, enableHorizontalClassMerging);
}
@@ -28,67 +27,75 @@
options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
- .enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
+ .addHorizontallyMergedClassesInspectorIf(
+ enableHorizontalClassMerging,
+ inspector ->
+ inspector.assertMergedInto(B.class, A.class).assertMergedInto(D.class, C.class))
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("bar", "foo c", "foo d", "foo c")
+ .assertSuccessWithOutputLines("A", "B", "foo: foo c", "B", "foo: bar d")
.inspect(
codeInspector -> {
assertThat(codeInspector.clazz(A.class), isPresent());
- assertThat(codeInspector.clazz(B.class), isPresent());
+ assertThat(
+ codeInspector.clazz(B.class), notIf(isPresent(), enableHorizontalClassMerging));
assertThat(codeInspector.clazz(C.class), isPresent());
assertThat(
codeInspector.clazz(D.class), notIf(isPresent(), enableHorizontalClassMerging));
});
}
- @NoVerticalClassMerging
- public abstract static class A {
- public abstract void foo();
+ @NeverClassInline
+ public static class A {
+ public A() {
+ System.out.println("A");
+ }
}
@NeverClassInline
public static class B {
+ public B() {
+ System.out.println("B");
+ }
+
+ public void foo(String s) {
+ System.out.println("foo: " + s);
+ }
+ }
+
+ @NeverClassInline
+ public static class C {
+ B b;
+
+ public C(B b) {
+ this.b = b;
+ }
+
+ @NeverInline
+ public void foo() {
+ b.foo("foo c");
+ }
+ }
+
+ @NeverClassInline
+ public static class D {
+ B b;
+
+ public D(B b) {
+ this.b = b;
+ }
+
@NeverInline
public void bar() {
- System.out.println("bar");
- }
- }
-
- @NeverClassInline
- public static class C extends A {
-
- @Override
- @NeverInline
- public void foo() {
- System.out.println("foo c");
- }
- }
-
- @NeverClassInline
- public static class D extends A {
-
- @Override
- @NeverInline
- public void foo() {
- System.out.println("foo d");
+ b.foo("bar d");
}
}
public static class Main {
- @NeverInline
- public static void foo(A a) {
- a.foo();
- }
-
public static void main(String[] args) {
- new B().bar();
- C c = new C();
-
- // This test also checks that the synthesized C#foo does not try to call the abstract A#foo.
- foo(c);
- foo(new D());
- c.foo();
+ new A();
+ new C(new B()).foo();
+ new D(new B()).bar();
}
}
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapMethodTest.java
index f0814bd..a531860 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapMethodTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapMethodTest.java
@@ -24,9 +24,7 @@
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
.addOptionsModification(
- options -> {
- options.enableHorizontalClassMerging = enableHorizontalClassMerging;
- })
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/MethodCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/MethodCollisionTest.java
new file mode 100644
index 0000000..4baa783
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/MethodCollisionTest.java
@@ -0,0 +1,115 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging.vertical;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MethodCollisionTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public MethodCollisionTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ // TODO(christofferqa): Currently we do not allow merging A into B because we find a
+ // collision. However, we are free to change the names of private methods, so we should
+ // handle them similar to fields (i.e., we should allow merging A into B). This would also
+ // improve the performance of the collision detector, because it would only have to
+ // consider non-private methods.
+ .addVerticallyMergedClassesInspector(
+ VerticallyMergedClassesInspector::assertNoClassesMerged)
+ .enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class);
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ B b = new B();
+ b.m();
+
+ D d = new D();
+ d.m();
+
+ // Ensure that the instantiations are not dead code eliminated.
+ escape(b);
+ escape(d);
+ }
+
+ @NeverInline
+ static void escape(Object o) {
+ if (System.currentTimeMillis() < 0) {
+ System.out.println(o);
+ }
+ }
+ }
+
+ @NoHorizontalClassMerging
+ public static class A {
+
+ // After class merging, this method will have the same signature as the method B.m,
+ // unless we handle the collision.
+ private A m() {
+ System.out.println("A.m");
+ return null;
+ }
+
+ public void invokeM() {
+ m();
+ }
+ }
+
+ public static class B extends A {
+
+ private B m() {
+ System.out.println("B.m");
+ invokeM();
+ return null;
+ }
+ }
+
+ @NoHorizontalClassMerging
+ public static class C {
+
+ // After class merging, this method will have the same signature as the method D.m,
+ // unless we handle the collision.
+ public C m() {
+ System.out.println("C.m");
+ return null;
+ }
+ }
+
+ public static class D extends C {
+
+ public D m() {
+ System.out.println("D.m");
+ super.m();
+ return null;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerIndirectReflectiveNameTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerIndirectReflectiveNameTest.java
new file mode 100644
index 0000000..f52ff7c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerIndirectReflectiveNameTest.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging.vertical;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThrows;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+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 VerticalClassMergerIndirectReflectiveNameTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withCfRuntimes()
+ .withDexRuntimesStartingFromIncluding(Version.V8_1_0)
+ .withAllApiLevelsAlsoForCf()
+ .build();
+ }
+
+ public VerticalClassMergerIndirectReflectiveNameTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(Main.class, A.class, B.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A::foo", "B::foo");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ // TODO(b/173099479): This should not throw an assertion-error.
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, A.class, B.class)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ diagnostics.assertErrorsMatch(
+ diagnosticMessage(
+ containsString(
+ "Expected vertically merged class"
+ + " `com.android.tools.r8.classmerging.vertical."
+ + "VerticalClassMergerIndirectReflectiveNameTest$A`"
+ + " to be absent")));
+ }));
+ }
+
+ public static class A {
+
+ @NeverInline
+ public void foo() {
+ System.out.println("A::foo");
+ }
+ }
+
+ @NeverClassInline
+ public static class B extends A {
+
+ @NeverInline
+ public void bar() {
+ System.out.println("B::foo");
+ }
+ }
+
+ public static class Main {
+
+ public static String getClassName() {
+ return "com.android.tools.r8.classmerging.vertical."
+ + "VerticalClassMergerIndirectReflectiveNameTest$A";
+ }
+
+ static {
+ try {
+ Class.forName(getClassName());
+ } catch (ClassNotFoundException e) {
+ }
+ }
+
+ public static void main(String[] args) {
+ B b = new B();
+ b.foo();
+ b.bar();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerReflectiveNameTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerReflectiveNameTest.java
new file mode 100644
index 0000000..da3165d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerReflectiveNameTest.java
@@ -0,0 +1,91 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging.vertical;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+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 VerticalClassMergerReflectiveNameTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withCfRuntimes()
+ .withDexRuntimesStartingFromIncluding(Version.V8_1_0)
+ .withAllApiLevelsAlsoForCf()
+ .build();
+ }
+
+ public VerticalClassMergerReflectiveNameTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(Main.class, A.class, B.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A::foo", "B::foo");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, A.class, B.class)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A::foo", "B::foo");
+ }
+
+ public static class A {
+
+ @NeverInline
+ public void foo() {
+ System.out.println("A::foo");
+ }
+ }
+
+ @NeverClassInline
+ public static class B extends A {
+
+ @NeverInline
+ public void bar() {
+ System.out.println("B::foo");
+ }
+ }
+
+ public static class Main {
+
+ private static final String className =
+ "com.android.tools.r8.classmerging.vertical.VerticalClassMergerReflectiveNameTest$A";
+
+ static {
+ try {
+ Class.forName(className);
+ } catch (ClassNotFoundException e) {
+ }
+ }
+
+ public static void main(String[] args) {
+ B b = new B();
+ b.foo();
+ b.bar();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index 4a3cc91..d7d31c9 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -387,37 +387,6 @@
}
@Test
- public void testMethodCollision() throws Throwable {
- String main = "classmerging.MethodCollisionTest";
- Path[] programFiles =
- new Path[] {
- CF_DIR.resolve("MethodCollisionTest.class"),
- CF_DIR.resolve("MethodCollisionTest$A.class"),
- CF_DIR.resolve("MethodCollisionTest$B.class"),
- CF_DIR.resolve("MethodCollisionTest$C.class"),
- CF_DIR.resolve("MethodCollisionTest$D.class")
- };
- // TODO(christofferqa): Currently we do not allow merging A into B because we find a collision.
- // However, we are free to change the names of private methods, so we should handle them similar
- // to fields (i.e., we should allow merging A into B). This would also improve the performance
- // of the collision detector, because it would only have to consider non-private methods.
- Set<String> preservedClassNames =
- ImmutableSet.of(
- "classmerging.MethodCollisionTest",
- "classmerging.MethodCollisionTest$A",
- "classmerging.MethodCollisionTest$B",
- "classmerging.MethodCollisionTest$C",
- "classmerging.MethodCollisionTest$D");
- runTest(
- testForR8(parameters.getBackend())
- .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
- .allowUnusedProguardConfigurationRules(),
- main,
- programFiles,
- preservedClassNames::contains);
- }
-
- @Test
public void testNestedDefaultInterfaceMethodsTest() throws Throwable {
String main = "classmerging.NestedDefaultInterfaceMethodsTest";
Path[] programFiles =
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 5856c44..ccfaeaf 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
@@ -4,6 +4,7 @@
package com.android.tools.r8.desugar.desugaredlibrary;
+import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue;
@@ -39,6 +40,9 @@
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
public class DesugaredLibraryTestBase extends TestBase {
@@ -63,7 +67,7 @@
}
protected boolean requiresEmulatedInterfaceCoreLibDesugaring(TestParameters parameters) {
- return parameters.getApiLevel().getLevel() < AndroidApiLevel.N.getLevel();
+ return parameters.getApiLevel().isLessThan(apiLevelWithDefaultInterfaceMethodsSupport());
}
protected boolean requiresAnyCoreLibDesugaring(TestParameters parameters) {
@@ -241,8 +245,7 @@
Path desugaredProgramClassFile, Path desugaredLibraryClassFile) throws Exception {
Path generatedKeepRules = temp.newFile().toPath();
TraceReferences.run(
- "--format",
- "keep",
+ "--keep-rules",
"--lib",
ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(),
"--target",
@@ -257,6 +260,48 @@
return FileUtils.readTextFile(generatedKeepRules, Charsets.UTF_8);
}
+ protected static ClassFileInfo extractClassFileInfo(byte[] classFileBytes) {
+ class ClassFileInfoExtractor extends ClassVisitor {
+ private String classBinaryName;
+ private List<String> interfaces = new ArrayList<>();
+ private final List<String> methodNames = new ArrayList<>();
+
+ private ClassFileInfoExtractor() {
+ super(ASM_VERSION);
+ }
+
+ @Override
+ public void visit(
+ int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ classBinaryName = name;
+ this.interfaces.addAll(Arrays.asList(interfaces));
+ super.visit(version, access, name, signature, superName, interfaces);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(
+ int access, String name, String desc, String signature, String[] exceptions) {
+ methodNames.add(name);
+ return super.visitMethod(access, name, desc, signature, exceptions);
+ }
+
+ ClassFileInfo getClassFileInfo() {
+ return new ClassFileInfo(classBinaryName, interfaces, methodNames);
+ }
+ }
+
+ ClassReader reader = new ClassReader(classFileBytes);
+ ClassFileInfoExtractor extractor = new ClassFileInfoExtractor();
+ reader.accept(
+ extractor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+ return extractor.getClassFileInfo();
+ }
+
public interface KeepRuleConsumer extends StringConsumer {
String get();
@@ -310,4 +355,28 @@
return result;
}
}
+
+ protected static class ClassFileInfo {
+ private final String classBinaryName;
+ private List<String> interfaces;
+ private final List<String> methodNames;
+
+ ClassFileInfo(String classBinaryNamename, List<String> interfaces, List<String> methodNames) {
+ this.classBinaryName = classBinaryNamename;
+ this.interfaces = interfaces;
+ this.methodNames = methodNames;
+ }
+
+ public String getClassBinaryName() {
+ return classBinaryName;
+ }
+
+ public List<String> getInterfaces() {
+ return interfaces;
+ }
+
+ public List<String> getMethodNames() {
+ return methodNames;
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/IteratorTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/IteratorTest.java
new file mode 100644
index 0000000..2a968a0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/IteratorTest.java
@@ -0,0 +1,209 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.fail;
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ZipUtils;
+import java.nio.file.Path;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Consumer;
+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 IteratorTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+ private final boolean canUseDefaultAndStaticInterfaceMethods;
+
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("1", "2", "3");
+
+ @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(),
+ getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
+ }
+
+ public IteratorTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ this.canUseDefaultAndStaticInterfaceMethods =
+ parameters
+ .getApiLevel()
+ .isGreaterThanOrEqualTo(apiLevelWithDefaultInterfaceMethodsSupport());
+ }
+
+ @Test
+ public void testIterator() throws Exception {
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addInnerClasses(IteratorTest.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ return;
+ }
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8()
+ .addInnerClasses(IteratorTest.class)
+ .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 testD8Cf() throws Exception {
+ // Use D8 to desugar with Java classfile output.
+ Path firstJar =
+ testForD8(Backend.CF)
+ .setMinApi(parameters.getApiLevel())
+ .addProgramClasses(Main.class, MyIterator.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), new AbsentKeepRuleConsumer())
+ .compile()
+ .writeToZip();
+
+ ClassFileInfo info =
+ extractClassFileInfo(
+ ZipUtils.readSingleEntry(firstJar, ZipUtils.zipEntryNameForClass(MyIterator.class)));
+ assertEquals(
+ MyIterator.class.getTypeName(),
+ DescriptorUtils.getJavaTypeFromBinaryName(info.getClassBinaryName()));
+ assertEquals(
+ canUseDefaultAndStaticInterfaceMethods ? 0 : 1,
+ info.getInterfaces().stream().filter(name -> name.equals("j$/util/Iterator")).count());
+ assertEquals(
+ canUseDefaultAndStaticInterfaceMethods ? 1 : 2,
+ info.getMethodNames().stream().filter(name -> name.equals("forEachRemaining")).count());
+
+ AndroidApiLevel apiLevelNotRequiringDesugaring = AndroidApiLevel.N;
+ if (parameters.getApiLevel().isLessThan(apiLevelNotRequiringDesugaring)) {
+ try {
+ // Use D8 to desugar with Java classfile output.
+ testForD8(Backend.CF)
+ .setMinApi(parameters.getApiLevel())
+ .addProgramFiles(firstJar)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), new AbsentKeepRuleConsumer())
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertErrorsMatch(
+ diagnosticMessage(
+ containsString(
+ "Code has already been library desugared. "
+ + "Interface Lj$/util/Iterator; is already implemented"))));
+ fail("Expected failure");
+ } catch (CompilationFailedException e) {
+ // Expected.
+ }
+ }
+
+ // Use D8 to desugar with Java classfile output.
+ Path secondJar =
+ testForD8(Backend.CF)
+ .addOptionsModification(
+ options ->
+ options.desugarSpecificOptions().allowDesugaredInput =
+ parameters.getApiLevel().isLessThan(apiLevelNotRequiringDesugaring))
+ .setMinApi(parameters.getApiLevel())
+ .addProgramFiles(firstJar)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), new AbsentKeepRuleConsumer())
+ .compile()
+ .writeToZip();
+
+ info =
+ extractClassFileInfo(
+ ZipUtils.readSingleEntry(secondJar, ZipUtils.zipEntryNameForClass(MyIterator.class)));
+ assertEquals(
+ MyIterator.class.getTypeName(),
+ DescriptorUtils.getJavaTypeFromBinaryName(info.getClassBinaryName()));
+ assertEquals(
+ canUseDefaultAndStaticInterfaceMethods ? 0 : 1,
+ info.getInterfaces().stream().filter(name -> name.equals("j$/util/Iterator")).count());
+ // TODO(b/171867367): This should only be 2.
+ assertEquals(
+ canUseDefaultAndStaticInterfaceMethods ? 1 : 3,
+ info.getMethodNames().stream().filter(name -> name.equals("forEachRemaining")).count());
+
+ if (parameters.getRuntime().isDex()) {
+ // Convert to DEX without desugaring and run.
+ testForD8()
+ .addProgramFiles(firstJar)
+ .setMinApi(parameters.getApiLevel())
+ .disableDesugaring()
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ collectKeepRulesWithTraceReferences(
+ firstJar, buildDesugaredLibraryClassFile(parameters.getApiLevel())),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ } else {
+ // Run on the JVM with desugared library on classpath.
+ testForJvm()
+ .addProgramFiles(firstJar)
+ .addRunClasspathFiles(buildDesugaredLibraryClassFile(parameters.getApiLevel()))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ Iterator<Integer> iterator = new MyIterator<>(1, 2, 3);
+ iterator.forEachRemaining(System.out::println);
+ }
+ }
+
+ static class MyIterator<E> implements Iterator<E> {
+
+ int index;
+ E[] items;
+
+ @SafeVarargs
+ public MyIterator(E... items) {
+ this.items = items;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return index < items.length;
+ }
+
+ @Override
+ public E next() {
+ return items[index++];
+ }
+
+ @Override
+ public void forEachRemaining(Consumer<? super E> action) {
+ while (hasNext()) {
+ action.accept(next());
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/CallBackConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/CallBackConversionTest.java
index 92d2394..210216f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/CallBackConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/CallBackConversionTest.java
@@ -11,7 +11,9 @@
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.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ZipUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import java.nio.file.Path;
@@ -104,7 +106,7 @@
@Test
public void testCallBackD8Cf() throws Exception {
// Use D8 to desugar with Java classfile output.
- Path jar =
+ Path firstJar =
testForD8(Backend.CF)
.setMinApi(parameters.getApiLevel())
.addProgramClasses(Impl.class)
@@ -114,9 +116,37 @@
.inspect(CallBackConversionTest::assertDuplicatedAPI)
.writeToZip();
+ ClassFileInfo info =
+ extractClassFileInfo(
+ ZipUtils.readSingleEntry(firstJar, ZipUtils.zipEntryNameForClass(Impl.class)));
+ assertEquals(
+ Impl.class.getTypeName(),
+ DescriptorUtils.getJavaTypeFromBinaryName(info.getClassBinaryName()));
+ assertEquals(2, info.getMethodNames().stream().filter(name -> name.equals("foo")).count());
+
+ // Use D8 to desugar with Java classfile output.
+ Path secondJar =
+ testForD8(Backend.CF)
+ .setMinApi(parameters.getApiLevel())
+ .addProgramFiles(firstJar)
+ .addLibraryClasses(CustomLibClass.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), new AbsentKeepRuleConsumer())
+ .compile()
+ .inspect(CallBackConversionTest::assertDuplicatedAPI)
+ .writeToZip();
+
+ info =
+ extractClassFileInfo(
+ ZipUtils.readSingleEntry(secondJar, ZipUtils.zipEntryNameForClass(Impl.class)));
+ assertEquals(
+ Impl.class.getTypeName(),
+ DescriptorUtils.getJavaTypeFromBinaryName(info.getClassBinaryName()));
+ // TODO(b/171867367): This should only be 2.
+ assertEquals(3, info.getMethodNames().stream().filter(name -> name.equals("foo")).count());
+
// Convert to DEX without desugaring and run.
testForD8()
- .addProgramFiles(jar)
+ .addProgramFiles(firstJar)
.setMinApi(parameters.getApiLevel())
.disableDesugaring()
.compile()
@@ -125,7 +155,7 @@
this::buildDesugaredLibrary,
parameters.getApiLevel(),
collectKeepRulesWithTraceReferences(
- jar, buildDesugaredLibraryClassFile(parameters.getApiLevel())),
+ firstJar, buildDesugaredLibraryClassFile(parameters.getApiLevel())),
shrinkDesugaredLibrary)
.addRunClasspathFiles(CUSTOM_LIB)
.run(parameters.getRuntime(), Impl.class)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonEnumTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonEnumTest.java
new file mode 100644
index 0000000..d71b536
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonEnumTest.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary.gson;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.lang.reflect.Field;
+import java.time.chrono.IsoEra;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class GsonEnumTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+
+ @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ public GsonEnumTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testCustomCollectionD8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8()
+ .addInnerClasses(GsonEnumTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutputLines("0");
+ }
+
+ @Test
+ public void testCustomCollectionR8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(Backend.DEX)
+ .addInnerClasses(GsonEnumTest.class)
+ .addKeepMainRule(Executor.class)
+ .noMinification()
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutputLines("0");
+ }
+
+ @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
+ static class Executor {
+ public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
+ // For GSON to correctly serialize enums, it needs to be able to access all of the static
+ // enum fields.
+ Field bce = IsoEra.class.getField("BCE");
+ System.out.println(((IsoEra) bce.get(null)).ordinal());
+ }
+ }
+}
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 b1a6811..b61e9d8 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
@@ -29,6 +29,8 @@
@RunWith(Parameterized.class)
public class R8CompiledThroughDexTest extends DesugaredLibraryTestBase {
+ private static final boolean testExternal = true;
+
private final TestParameters parameters;
@Parameters(name = "{0}")
@@ -69,34 +71,41 @@
File ouputFolder = temp.newFolder("output");
// Compile R8 to dex on the JVM.
- Path ouputThroughCf = ouputFolder.toPath().resolve("outThroughCf.zip").toAbsolutePath();
- ProcessResult javaProcessResult =
- ToolHelper.runJava(
- TestRuntime.getCheckedInJdk9(),
- Collections.singletonList(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR),
- "-Xmx512m",
- R8.class.getTypeName(),
- "--release",
- "--min-api",
- Integer.toString(parameters.getApiLevel().getLevel()),
- "--output",
- ouputThroughCf.toString(),
- "--lib",
- ToolHelper.JAVA_8_RUNTIME,
- "--pg-conf",
- R8_KEEP,
- ToolHelper.R8_WITH_RELOCATED_DEPS_JAR.toAbsolutePath().toString());
- if (javaProcessResult.exitCode != 0) {
- System.out.println(javaProcessResult);
+ Path outputThroughCf = ouputFolder.toPath().resolve("outThroughCf.zip").toAbsolutePath();
+ if (testExternal) {
+ ProcessResult javaProcessResult =
+ ToolHelper.runJava(
+ TestRuntime.getCheckedInJdk9(),
+ Collections.singletonList(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR),
+ "-Xmx512m",
+ R8.class.getTypeName(),
+ "--release",
+ "--min-api",
+ Integer.toString(parameters.getApiLevel().getLevel()),
+ "--output",
+ outputThroughCf.toString(),
+ "--lib",
+ ToolHelper.JAVA_8_RUNTIME,
+ "--pg-conf",
+ R8_KEEP,
+ ToolHelper.R8_WITH_RELOCATED_DEPS_JAR.toAbsolutePath().toString());
+ assertEquals(javaProcessResult.toString(), 0, javaProcessResult.exitCode);
+ } else {
+ testForR8(parameters.getBackend())
+ .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
+ .addLibraryFiles(ToolHelper.getJava8RuntimeJar())
+ .addKeepRuleFiles(Paths.get(R8_KEEP))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .writeToZip(outputThroughCf);
}
- assertEquals(0, javaProcessResult.exitCode);
// Compile R8 to Dex on Dex, using the previous dex artifact.
// We need the extra parameter --64 to use 64 bits frameworks.
Path ouputThroughDex = ouputFolder.toPath().resolve("outThroughDex.zip").toAbsolutePath();
ProcessResult artProcessResult =
ToolHelper.runArtRaw(
- Collections.singletonList(ouputThroughCf.toAbsolutePath().toString()),
+ Collections.singletonList(outputThroughCf.toAbsolutePath().toString()),
R8.class.getTypeName(),
(ToolHelper.ArtCommandBuilder builder) ->
builder.appendArtOption("--64").appendArtOption("-Xmx512m"),
@@ -118,6 +127,6 @@
assertEquals(0, artProcessResult.exitCode);
// Ensure both generated artifacts are equal.
- assertTrue(BootstrapCurrentEqualityTest.filesAreEqual(ouputThroughCf, ouputThroughDex));
+ assertTrue(BootstrapCurrentEqualityTest.filesAreEqual(outputThroughCf, ouputThroughDex));
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/softverificationerrorremoval/GetDeclaredMethodsErrorRemovalTest.java b/src/test/java/com/android/tools/r8/desugar/softverificationerrorremoval/GetDeclaredMethodsErrorRemovalTest.java
new file mode 100644
index 0000000..2b72a6f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/softverificationerrorremoval/GetDeclaredMethodsErrorRemovalTest.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.softverificationerrorremoval;
+
+import static com.android.tools.r8.TestRuntime.CfRuntime.getCheckedInJdk8;
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.D8TestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.function.Supplier;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class GetDeclaredMethodsErrorRemovalTest extends TestBase {
+
+ private final TestParameters parameters;
+ private static final String TYPE =
+ "com.android.tools.r8.desugar.softverificationerrorremoval."
+ + "GetDeclaredMethodsErrorRemovalTest";
+ private static final String EXPECTED_RESULT =
+ "[void"
+ + " "
+ + TYPE
+ + "$ExampleClass.hello(),"
+ + " void"
+ + " "
+ + TYPE
+ + "$ExampleClass.hello(java.util.function.Supplier)]";
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public GetDeclaredMethodsErrorRemovalTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testWithoutJavaStub() throws Exception {
+ D8TestRunResult run =
+ testForD8()
+ .addInnerClasses(GetDeclaredMethodsErrorRemovalTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class);
+ if (parameters.getDexRuntimeVersion().isOlderThanOrEqual(ToolHelper.DexVm.Version.V6_0_1)) {
+ run.assertFailureWithErrorThatMatches(containsString("java.lang.NoClassDefFoundError"));
+ } else {
+ run.assertSuccessWithOutputLines(EXPECTED_RESULT);
+ }
+ }
+
+ @Test
+ public void testWithJavaStub() throws Exception {
+ Path stubs =
+ javac(getCheckedInJdk8())
+ .addSourceFiles(Paths.get("src/test/javaStubs/Supplier.java"))
+ .compile();
+ testForD8()
+ .addInnerClasses(GetDeclaredMethodsErrorRemovalTest.class)
+ .addProgramFiles(stubs)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines(EXPECTED_RESULT);
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ System.out.println(Arrays.toString(ExampleClass.class.getDeclaredMethods()));
+ }
+ }
+
+ static class ExampleClass {
+ void hello() {
+ System.out.println("hello");
+ }
+
+ void hello(Supplier<String> stringSupplier) {
+ System.out.println(stringSupplier.get());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/softverificationerrorremoval/SoftVerificationErrorRemovalTest.java b/src/test/java/com/android/tools/r8/desugar/softverificationerrorremoval/SoftVerificationErrorRemovalTest.java
new file mode 100644
index 0000000..1a1720e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/softverificationerrorremoval/SoftVerificationErrorRemovalTest.java
@@ -0,0 +1,94 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.softverificationerrorremoval;
+
+import static com.android.tools.r8.TestRuntime.CfRuntime.getCheckedInJdk8;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.D8TestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.function.Supplier;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class SoftVerificationErrorRemovalTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public SoftVerificationErrorRemovalTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testWithoutJavaStub() throws Exception {
+ D8TestRunResult run =
+ testForD8()
+ .addInnerClasses(SoftVerificationErrorRemovalTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class);
+ assertVerificationErrorsPresent(
+ run.getStdErr(),
+ parameters.getDexRuntimeVersion().isOlderThanOrEqual(ToolHelper.DexVm.Version.V4_4_4));
+ }
+
+ private void assertVerificationErrorsPresent(String stdErr, boolean present) {
+ assertEquals(
+ present,
+ stdErr.contains(
+ "VFY: unable to find class referenced in signature (Ljava/util/function/Supplier;)"));
+ assertEquals(
+ present,
+ stdErr.contains(
+ "VFY: unable to resolve interface method 7: Ljava/util/function/Supplier;.get"
+ + " ()Ljava/lang/Object;"));
+ }
+
+ @Test
+ public void testWithJavaStub() throws Exception {
+ Path stubs =
+ javac(getCheckedInJdk8())
+ .addSourceFiles(Paths.get("src/test/javaStubs/Supplier.java"))
+ .compile();
+ D8TestRunResult run =
+ testForD8()
+ .addInnerClasses(SoftVerificationErrorRemovalTest.class)
+ .addProgramFiles(stubs)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class);
+ assertVerificationErrorsPresent(run.getStdErr(), false);
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ ExampleClass exampleClass = new ExampleClass();
+ exampleClass.hello();
+ }
+ }
+
+ static class ExampleClass {
+ void hello() {
+ System.out.println("hello");
+ }
+
+ void hello(Supplier<String> stringSupplier) {
+ System.out.println(stringSupplier.get());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
index b48e0a93..0fe19bc 100644
--- a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
+++ b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.dex;
import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexDebugEvent;
import com.android.tools.r8.graph.DexDebugInfo;
@@ -23,9 +24,11 @@
private ObjectToOffsetMapping emptyObjectTObjectMapping() {
return new ObjectToOffsetMapping(
- AppInfo.createInitialAppInfo(
- DexApplication.builder(new InternalOptions(new DexItemFactory(), new Reporter()), null)
- .build()),
+ AppView.createForD8(
+ AppInfo.createInitialAppInfo(
+ DexApplication.builder(
+ new InternalOptions(new DexItemFactory(), new Reporter()), null)
+ .build())),
GraphLens.getIdentityLens(),
NamingLens.getIdentityLens(),
InitClassLens.getDefault(),
diff --git a/src/test/java/com/android/tools/r8/dex/DexStringTest.java b/src/test/java/com/android/tools/r8/dex/DexStringTest.java
index b12d876..ad41738 100644
--- a/src/test/java/com/android/tools/r8/dex/DexStringTest.java
+++ b/src/test/java/com/android/tools/r8/dex/DexStringTest.java
@@ -78,8 +78,7 @@
private void check(int expected, DexString s1, DexString s2) {
assertEquals(s1.dump() + " " + s2.dump(),
expected, Integer.signum(s1.toString().compareTo(s2.toString())));
- assertEquals(s1.dump() + " " + s2.dump(),
- expected, Integer.signum(s1.slowCompareTo(s2)));
+ assertEquals(s1.dump() + " " + s2.dump(), expected, Integer.signum(s1.compareTo(s2)));
}
private void checkEncodedLength(DexString s, int encodedLength) {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java
index ab837d3..c494152 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingTestBase.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringUtils;
@@ -86,10 +87,12 @@
}
static List<Object[]> enumUnboxingTestParameters() {
- return buildParameters(
- getTestParameters().withDexRuntimes().withAllApiLevels().build(),
- BooleanUtils.values(),
- getAllEnumKeepRules());
+ return enumUnboxingTestParameters(
+ getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ static List<Object[]> enumUnboxingTestParameters(TestParametersCollection testParameters) {
+ return buildParameters(testParameters, BooleanUtils.values(), getAllEnumKeepRules());
}
protected static EnumKeepRules[] getAllEnumKeepRules() {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/LambdaEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/LambdaEnumUnboxingTest.java
new file mode 100644
index 0000000..d6eb6d1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/LambdaEnumUnboxingTest.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.enumunboxing;
+
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class LambdaEnumUnboxingTest extends EnumUnboxingTestBase {
+
+ private final TestParameters parameters;
+ private final boolean enumValueOptimization;
+ private final EnumKeepRules enumKeepRules;
+
+ @Parameters(name = "{0} valueOpt: {1} keep: {2}")
+ public static List<Object[]> data() {
+ return enumUnboxingTestParameters(getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ public LambdaEnumUnboxingTest(
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
+ this.parameters = parameters;
+ this.enumValueOptimization = enumValueOptimization;
+ this.enumKeepRules = enumKeepRules;
+ }
+
+ @Test
+ public void testEnumUnboxing() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addOptionsModification(
+ options -> {
+ if (options.isGeneratingClassFiles()) {
+ // TODO(b/172568606): Remove this when enabled for CF by default.
+ assertFalse(options.enableEnumUnboxing);
+ options.enableEnumUnboxing = true;
+ }
+ })
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .addOptionsModification(options -> options.testing.enableEnumUnboxingDebugLogs = false)
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("0", "0", "1", "0", "0");
+ }
+
+ @NeverClassInline
+ enum MyEnum {
+ A,
+ B,
+ C
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(MyEnum.A.ordinal());
+ boolean[] booleans = new boolean[] {true, false};
+ forEach(booleans, Main::printAndGetEnum);
+ System.out.println(printAndGetEnum(true).ordinal());
+ }
+
+ @NeverInline
+ private static MyEnum printAndGetEnum(boolean b) {
+ MyEnum myEnum = b ? MyEnum.A : MyEnum.B;
+ System.out.println(myEnum.ordinal());
+ return myEnum;
+ }
+
+ @NeverInline
+ static void forEach(boolean[] booleans, MyBooleanConsumer consumer) {
+ for (boolean b : booleans) {
+ consumer.accept(b);
+ }
+ }
+ }
+
+ interface MyBooleanConsumer {
+
+ void accept(Boolean b);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/FieldSignatureTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/FieldSignatureTest.java
index b6e34ad..81d8e11 100644
--- a/src/test/java/com/android/tools/r8/graph/genericsignature/FieldSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/FieldSignatureTest.java
@@ -6,8 +6,11 @@
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static com.google.common.base.Predicates.alwaysFalse;
+import static com.google.common.base.Predicates.alwaysTrue;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessages;
@@ -18,9 +21,11 @@
import com.android.tools.r8.graph.GenericSignature;
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
import com.android.tools.r8.graph.GenericSignaturePrinter;
+import com.android.tools.r8.graph.GenericSignatureTypeRewriter;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.Reporter;
+import java.util.function.Function;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -106,4 +111,18 @@
assertEquals(FieldTypeSignature.noSignature(), parsed);
return testDiagnosticMessages;
}
+
+ @Test
+ public void testPruningNullTest() {
+ DexItemFactory factory = new DexItemFactory();
+ FieldTypeSignature parsed =
+ GenericSignature.parseFieldTypeSignature(
+ "A", "Lfoo/bar/Baz;", Origin.unknown(), factory, new Reporter());
+ assertTrue(parsed.hasSignature());
+ GenericSignatureTypeRewriter rewriter =
+ new GenericSignatureTypeRewriter(factory, alwaysTrue(), Function.identity(), null);
+ FieldTypeSignature rewrittenType = rewriter.rewrite(parsed);
+ assertNotNull(rewrittenType);
+ assertTrue(rewrittenType.hasNoSignature());
+ }
}
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 ddaf85f..318de07 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
@@ -30,6 +30,7 @@
public ChromeProtoRewritingTest(TestParameters parameters) {
super(200430, false);
+ parameters.assertNoneRuntime();
}
@Test
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/InvokeMultiNewArraySideEffectTest.java b/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/InvokeMultiNewArraySideEffectTest.java
index 487d60a..fbe9bd8 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/InvokeMultiNewArraySideEffectTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/InvokeMultiNewArraySideEffectTest.java
@@ -9,6 +9,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.StringContains.containsString;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -25,7 +26,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public InvokeMultiNewArraySideEffectTest(TestParameters parameters) {
@@ -37,7 +38,8 @@
testForR8(parameters.getBackend())
.addInnerClasses(InvokeMultiNewArraySideEffectTest.class)
.addKeepMainRule(TestClass.class)
- .setMinApi(parameters.getRuntime())
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
.compile()
.inspect(
inspector -> {
@@ -66,7 +68,9 @@
}
}
+ @NoHorizontalClassMerging
static class A {}
+ @NoHorizontalClassMerging
static class B {}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index 807b061..af047b9 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -13,10 +13,14 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.ir.optimize.enums.EnumUnboxingRewriter;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ZipUtils;
@@ -58,6 +62,7 @@
private final boolean allowAccessModification;
private final TestParameters parameters;
private Path outputDir = null;
+ private String nullabilityClass = "inlining.Nullability";
public R8InliningTest(boolean allowAccessModification, TestParameters parameters) {
this.allowAccessModification = allowAccessModification;
@@ -94,6 +99,14 @@
return null;
}
+ private void fixInliningNullabilityClass(
+ DexItemFactory dexItemFactory, HorizontallyMergedClasses horizontallyMergedClasses) {
+ DexType originalType =
+ dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor("inlining.Nullability"));
+ nullabilityClass =
+ horizontallyMergedClasses.getMergeTargetOrDefault(originalType).toSourceString();
+ }
+
private void generateR8Version(Path out, Path mapFile, boolean inlining) throws Exception {
assert parameters.isDexRuntime() || parameters.isCfRuntime();
R8Command.Builder commandBuilder =
@@ -126,6 +139,7 @@
// Tests depend on nullability of receiver and argument in general. Learning very accurate
// nullability from actual usage in tests bothers what we want to test.
o.callSiteOptimizationOptions().disableTypePropagationForTesting();
+ o.testing.horizontallyMergedClassesConsumer = this::fixInliningNullabilityClass;
});
}
@@ -201,7 +215,7 @@
// a private class in another package.
checkAbsent(clazz, "int", "callInterfaceMethod", ImmutableList.of("inlining.IFace"));
- clazz = inspector.clazz("inlining.Nullability");
+ clazz = inspector.clazz(nullabilityClass);
checkAbsentBooleanMethod(clazz, "inlinableWithPublicField");
checkAbsentBooleanMethod(clazz, "inlinableWithControlFlow");
}
@@ -309,7 +323,7 @@
final int INLINABLE = allowAccessModification ? 0 : 1;
final int NEVER_INLINABLE = 1;
- ClassSubject clazz = inspector.clazz("inlining.Nullability");
+ ClassSubject clazz = inspector.clazz(nullabilityClass);
MethodSubject m;
m = clazz.method("int", "inlinable", ImmutableList.of("inlining.A"));
@@ -339,7 +353,7 @@
public void invokeOnNonNullReceiver() throws Exception {
CodeInspector inspector =
new CodeInspector(getGeneratedFiles(), getGeneratedProguardMap(), null);
- ClassSubject clazz = inspector.clazz("inlining.Nullability");
+ ClassSubject clazz = inspector.clazz(nullabilityClass);
MethodSubject m = clazz.method("int", "conditionalOperator", ImmutableList.of("inlining.A"));
assertTrue(m.isPresent());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerSimplePairBuilderTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerSimplePairBuilderTest.java
index f751e25..1d1f00e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerSimplePairBuilderTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerSimplePairBuilderTest.java
@@ -10,6 +10,7 @@
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.StringUtils;
@@ -57,6 +58,7 @@
// TODO(b/143129517): This relies on PairBuilder::build being inlined, thus the limit of 6.
.addOptionsModification(options -> options.inliningInstructionLimit = 6)
.enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.noMinification()
.setMinApi(parameters.getApiLevel())
.compile()
@@ -138,6 +140,7 @@
}
}
+ @NoHorizontalClassMerging
static class PairBuilder<F, S> {
F first;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
index 8d73c3a..cf52085 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
@@ -129,8 +129,7 @@
Collections.emptySet(), collectTypes(clazz.uniqueMethodWithName("testCallOnIface1")));
assertEquals(
- Collections.singleton("com.android.tools.r8.ir.optimize.classinliner.trivial.Iface2Impl"),
- collectTypes(clazz.uniqueMethodWithName("testCallOnIface2")));
+ Collections.emptySet(), collectTypes(clazz.uniqueMethodWithName("testCallOnIface2")));
assertEquals(
Sets.newHashSet(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/cost/NonMaterializingFieldAccessesAfterClassInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/cost/NonMaterializingFieldAccessesAfterClassInliningTest.java
index c95fa85..9df6cb2 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/cost/NonMaterializingFieldAccessesAfterClassInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/cost/NonMaterializingFieldAccessesAfterClassInliningTest.java
@@ -9,6 +9,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -40,6 +41,7 @@
// Should be able to class inline Builder even when the threshold is low.
.addOptionsModification(options -> options.classInliningInstructionAllowance = 3)
.enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
@@ -121,6 +123,7 @@
}
}
+ @NoHorizontalClassMerging
static class Builder {
char c1;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/CycleReferenceAB.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/CycleReferenceAB.java
index d26641a..0390712 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/CycleReferenceAB.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/CycleReferenceAB.java
@@ -4,6 +4,9 @@
package com.android.tools.r8.ir.optimize.classinliner.trivial;
+import com.android.tools.r8.NoHorizontalClassMerging;
+
+@NoHorizontalClassMerging
public class CycleReferenceAB {
private String a;
diff --git a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
index 177a8a5..f76758c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.kotlin;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -14,8 +15,9 @@
import static org.junit.Assert.fail;
import com.android.tools.r8.KotlinTestBase;
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.R8Command;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.graph.Code;
@@ -23,22 +25,18 @@
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FieldSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
-import java.util.function.Consumer;
import org.junit.Assume;
public abstract class AbstractR8KotlinTestBase extends KotlinTestBase {
@@ -217,30 +215,6 @@
return code.asDexCode();
}
- private String buildProguardRules(String mainClass) {
- ProguardRulesBuilder proguardRules = new ProguardRulesBuilder();
- proguardRules.appendWithLineSeparator(keepMainProguardConfiguration(mainClass));
- proguardRules.dontObfuscate();
- if (allowAccessModification) {
- proguardRules.allowAccessModification();
- }
- return proguardRules.toString();
- }
-
- protected String keepAllMembers(String className) {
- return StringUtils.lines(
- "-keep class " + className + " {",
- " *;",
- "}");
- }
-
- protected String keepMainMethod(String className) {
- return StringUtils.lines(
- "-keepclasseswithmembers class " + className + " {",
- " public static void main(...);",
- "}");
- }
-
protected String keepClassMethod(String className, MethodSignature methodSignature) {
return StringUtils.lines(
"-keep class " + className + " {",
@@ -255,54 +229,21 @@
"}");
}
- protected void runTest(String folder, String mainClass,
- AndroidAppInspector inspector) throws Exception {
- runTest(folder, mainClass, null, null, inspector);
+ protected R8TestRunResult runTest(String folder, String mainClass) throws Exception {
+ return runTest(folder, mainClass, null);
}
- protected void runTest(String folder, String mainClass,
- Consumer<InternalOptions> optionsConsumer, AndroidAppInspector inspector) throws Exception {
- runTest(folder, mainClass, null, optionsConsumer, inspector);
- }
-
- protected void runTest(String folder, String mainClass,
- String extraProguardRules, AndroidAppInspector inspector) throws Exception {
- runTest(folder, mainClass, extraProguardRules, null, inspector);
- }
-
- protected void runTest(String folder, String mainClass, String extraProguardRules,
- Consumer<InternalOptions> optionsConsumer, AndroidAppInspector inspector) throws Exception {
+ protected R8TestRunResult runTest(
+ String folder, String mainClass, ThrowableConsumer<R8FullTestBuilder> configuration)
+ throws Exception {
Assume.assumeTrue(ToolHelper.artSupported() || ToolHelper.compareAgaintsGoldenFiles());
- String proguardRules = buildProguardRules(mainClass);
- if (extraProguardRules != null) {
- proguardRules += extraProguardRules;
- }
-
// Build classpath for compilation (and java execution)
classpath.clear();
classpath.add(getKotlinJarFile(folder));
classpath.add(getJavaJarFile(folder));
classpath.addAll(extraClasspath);
- // Build with R8
- AndroidApp.Builder builder = AndroidApp.builder();
- builder.addProgramFiles(classpath);
- R8Command.Builder commandBuilder =
- ToolHelper.prepareR8CommandBuilder(builder.build(), emptyConsumer(Backend.DEX))
- .addLibraryFiles(runtimeJar(Backend.DEX))
- .addProguardConfiguration(ImmutableList.of(proguardRules), Origin.unknown());
- ToolHelper.allowTestProguardOptions(commandBuilder);
- AndroidApp app = ToolHelper.runR8(commandBuilder.build(), optionsConsumer);
-
- // Materialize file for execution.
- Path generatedDexFile = temp.getRoot().toPath().resolve("classes.jar");
- app.writeToZip(generatedDexFile, OutputMode.DexIndexed);
-
- // Run with ART.
- String artOutput =
- ToolHelper.runArtNoVerificationErrors(generatedDexFile.toString(), mainClass);
-
// Compare with Java.
ToolHelper.ProcessResult javaResult = ToolHelper.runJava(classpath, mainClass);
if (javaResult.exitCode != 0) {
@@ -310,11 +251,22 @@
System.err.println(javaResult.stderr);
fail("JVM failed for: " + mainClass);
}
- assertEquals("JVM and ART output differ", javaResult.stdout, artOutput);
- if (inspector != null) {
- inspector.inspectApp(app);
- }
+ // Build with R8
+ return testForR8(Backend.DEX)
+ .addProgramFiles(classpath)
+ .addKeepMainRule(mainClass)
+ .allowAccessModification(allowAccessModification)
+ .allowDiagnosticMessages()
+ .enableProguardTestOptions()
+ .noMinification()
+ .apply(configuration)
+ .compile()
+ .assertAllWarningMessagesMatch(
+ containsString("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .assertAllInfoMessagesMatch(containsString("Unrecognized Kotlin lambda "))
+ .run(mainClass)
+ .assertSuccessWithOutput(javaResult.stdout);
}
protected void checkClassExistsInInput(String className) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index d8419ca..2bb0867 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -12,6 +12,9 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.code.NewInstance;
import com.android.tools.r8.code.SgetObject;
@@ -20,7 +23,6 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -78,184 +80,216 @@
public void testJStyleLambdas() throws Exception {
assumeTrue("Only work with -allowaccessmodification", allowAccessModification);
final String mainClassName = "class_inliner_lambda_j_style.MainKt";
- runTest(
- "class_inliner_lambda_j_style",
- mainClassName,
- false,
- app -> {
- CodeInspector inspector = new CodeInspector(app);
- assertThat(
- inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful$1"), isPresent());
- assertThat(
- inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful2$1"), isPresent());
- assertThat(
- inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful3$1"), isPresent());
- });
+ runTestWithDefaults(
+ "class_inliner_lambda_j_style",
+ mainClassName,
+ testBuilder ->
+ testBuilder
+ // TODO(jsjeon): Introduce @NeverInline to kotlinR8TestResources
+ .addKeepRules("-neverinline class * { void test*State*(...); }")
+ .noClassInlining())
+ .inspect(
+ inspector -> {
+ assertThat(
+ inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful$1"),
+ isPresent());
+ assertThat(
+ inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful2$1"),
+ isPresent());
+ assertThat(
+ inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful3$1"),
+ isPresent());
+ });
- runTest(
- "class_inliner_lambda_j_style",
- mainClassName,
- true,
- app -> {
- CodeInspector inspector = new CodeInspector(app);
- Predicate<DexType> lambdaCheck = createLambdaCheck(inspector);
- ClassSubject clazz = inspector.clazz(mainClassName);
+ runTestWithDefaults(
+ "class_inliner_lambda_j_style",
+ mainClassName,
+ testBuilder ->
+ testBuilder
+ // TODO(jsjeon): Introduce @NeverInline to kotlinR8TestResources
+ .addKeepRules("-neverinline class * { void test*State*(...); }"))
+ .inspect(
+ inspector -> {
+ Predicate<DexType> lambdaCheck = createLambdaCheck(inspector);
+ ClassSubject clazz = inspector.clazz(mainClassName);
- assertEquals(
- Sets.newHashSet(), collectAccessedTypes(lambdaCheck, clazz, "testStateless"));
+ assertEquals(
+ Sets.newHashSet(), collectAccessedTypes(lambdaCheck, clazz, "testStateless"));
- assertEquals(Sets.newHashSet(), collectAccessedTypes(lambdaCheck, clazz, "testStateful"));
+ assertEquals(
+ Sets.newHashSet(), collectAccessedTypes(lambdaCheck, clazz, "testStateful"));
- assertThat(
- inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful$1"),
- not(isPresent()));
+ assertThat(
+ inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful$1"),
+ not(isPresent()));
- assertEquals(
- Sets.newHashSet(), collectAccessedTypes(lambdaCheck, clazz, "testStateful2"));
+ assertEquals(
+ Sets.newHashSet(), collectAccessedTypes(lambdaCheck, clazz, "testStateful2"));
- assertThat(
- inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful2$1"),
- not(isPresent()));
+ assertThat(
+ inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful2$1"),
+ not(isPresent()));
- assertEquals(
- Sets.newHashSet(), collectAccessedTypes(lambdaCheck, clazz, "testStateful3"));
+ assertEquals(
+ Sets.newHashSet(), collectAccessedTypes(lambdaCheck, clazz, "testStateful3"));
- assertThat(
- inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful3$1"),
- not(isPresent()));
- });
+ assertThat(
+ inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful3$1"),
+ not(isPresent()));
+ });
}
@Test
public void testKStyleLambdas() throws Exception {
assumeTrue("Only work with -allowaccessmodification", allowAccessModification);
final String mainClassName = "class_inliner_lambda_k_style.MainKt";
- runTest(
- "class_inliner_lambda_k_style",
- mainClassName,
- false,
- app -> {
- CodeInspector inspector = new CodeInspector(app);
- assertThat(
- inspector.clazz("class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateless$1"),
- isPresent());
- assertThat(
- inspector.clazz("class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateful$1"),
- isPresent());
- assertThat(
- inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod$1"),
- isPresent());
- assertThat(
- inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod2$1"),
- isPresent());
- assertThat(
- inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod3$1"),
- isPresent());
- assertThat(
- inspector.clazz(
- "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda$1"),
- isPresent());
- assertThat(
- inspector.clazz(
- "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda2$1"),
- isPresent());
- assertThat(
- inspector.clazz(
- "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda3$1"),
- isPresent());
- });
+ runTestWithDefaults(
+ "class_inliner_lambda_k_style",
+ mainClassName,
+ testBuilder ->
+ testBuilder
+ // TODO(jsjeon): Introduce @NeverInline to kotlinR8TestResources
+ .addKeepRules(
+ "-neverinline class * { void test*State*(...); }",
+ "-neverinline class * { void testBigExtraMethod(...); }",
+ "-neverinline class * { void testBigExtraMethodReturningLambda(...); }")
+ .noClassInlining())
+ .inspect(
+ inspector -> {
+ assertThat(
+ inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateless$1"),
+ isPresent());
+ assertThat(
+ inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateful$1"),
+ isPresent());
+ assertThat(
+ inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod$1"),
+ isPresent());
+ assertThat(
+ inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod2$1"),
+ isPresent());
+ assertThat(
+ inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod3$1"),
+ isPresent());
+ assertThat(
+ inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda$1"),
+ isPresent());
+ assertThat(
+ inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda2$1"),
+ isPresent());
+ assertThat(
+ inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda3$1"),
+ isPresent());
+ });
- runTest(
- "class_inliner_lambda_k_style",
- mainClassName,
- true,
- app -> {
- CodeInspector inspector = new CodeInspector(app);
- Predicate<DexType> lambdaCheck = createLambdaCheck(inspector);
- ClassSubject clazz = inspector.clazz(mainClassName);
+ runTestWithDefaults(
+ "class_inliner_lambda_k_style",
+ mainClassName,
+ testBuilder ->
+ testBuilder
+ // TODO(jsjeon): Introduce @NeverInline to kotlinR8TestResources
+ .addKeepRules(
+ "-neverinline class * { void test*State*(...); }",
+ "-neverinline class * { void testBigExtraMethod(...); }",
+ "-neverinline class * { void testBigExtraMethodReturningLambda(...); }"))
+ .inspect(
+ inspector -> {
+ Predicate<DexType> lambdaCheck = createLambdaCheck(inspector);
+ ClassSubject clazz = inspector.clazz(mainClassName);
- assertEquals(
- Sets.newHashSet(),
- collectAccessedTypes(
- lambdaCheck, clazz, "testKotlinSequencesStateless", "kotlin.sequences.Sequence"));
+ assertEquals(
+ Sets.newHashSet(),
+ collectAccessedTypes(
+ lambdaCheck,
+ clazz,
+ "testKotlinSequencesStateless",
+ "kotlin.sequences.Sequence"));
- assertThat(
- inspector.clazz("class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateless$1"),
- not(isPresent()));
+ assertThat(
+ inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateless$1"),
+ not(isPresent()));
- assertEquals(
- Sets.newHashSet(),
- collectAccessedTypes(
- lambdaCheck,
- clazz,
- "testKotlinSequencesStateful",
- "int",
- "int",
- "kotlin.sequences.Sequence"));
+ assertEquals(
+ Sets.newHashSet(),
+ collectAccessedTypes(
+ lambdaCheck,
+ clazz,
+ "testKotlinSequencesStateful",
+ "int",
+ "int",
+ "kotlin.sequences.Sequence"));
- assertThat(
- inspector.clazz("class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateful$1"),
- not(isPresent()));
+ assertThat(
+ inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateful$1"),
+ not(isPresent()));
- assertEquals(
- Sets.newHashSet(), collectAccessedTypes(lambdaCheck, clazz, "testBigExtraMethod"));
+ assertEquals(
+ Sets.newHashSet(),
+ collectAccessedTypes(lambdaCheck, clazz, "testBigExtraMethod"));
- assertThat(
- inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod$1"),
- not(isPresent()));
- assertThat(
- inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod2$1"),
- not(isPresent()));
- assertThat(
- inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod3$1"),
- not(isPresent()));
+ assertThat(
+ inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod$1"),
+ not(isPresent()));
+ assertThat(
+ inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod2$1"),
+ not(isPresent()));
+ assertThat(
+ inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod3$1"),
+ not(isPresent()));
- assertEquals(
- Sets.newHashSet(),
- collectAccessedTypes(lambdaCheck, clazz, "testBigExtraMethodReturningLambda"));
+ assertEquals(
+ Sets.newHashSet(),
+ collectAccessedTypes(lambdaCheck, clazz, "testBigExtraMethodReturningLambda"));
- assertThat(
- inspector.clazz(
- "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda$1"),
- not(isPresent()));
- assertThat(
- inspector.clazz(
- "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda2$1"),
- not(isPresent()));
- assertThat(
- inspector.clazz(
- "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda3$1"),
- not(isPresent()));
- });
+ assertThat(
+ inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda$1"),
+ not(isPresent()));
+ assertThat(
+ inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda2$1"),
+ not(isPresent()));
+ assertThat(
+ inspector.clazz(
+ "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda3$1"),
+ not(isPresent()));
+ });
}
@Test
public void testDataClass() throws Exception {
assumeTrue("Only work with -allowaccessmodification", allowAccessModification);
final String mainClassName = "class_inliner_data_class.MainKt";
- runTest(
- "class_inliner_data_class",
- mainClassName,
- true,
- app -> {
- CodeInspector inspector = new CodeInspector(app);
- ClassSubject clazz = inspector.clazz(mainClassName);
- assertTrue(
- collectAccessedTypes(
- type -> !type.toSourceString().startsWith("java."),
- clazz,
- "main",
- String[].class.getCanonicalName())
- .isEmpty());
- assertEquals(
- Lists.newArrayList(
- "void kotlin.jvm.internal.Intrinsics.throwParameterIsNullException(java.lang.String)"),
- collectStaticCalls(clazz, "main", String[].class.getCanonicalName()));
- });
+ runTestWithDefaults("class_inliner_data_class", mainClassName)
+ .inspect(
+ inspector -> {
+ ClassSubject clazz = inspector.clazz(mainClassName);
+ assertTrue(
+ collectAccessedTypes(
+ type -> !type.toSourceString().startsWith("java."),
+ clazz,
+ "main",
+ String[].class.getCanonicalName())
+ .isEmpty());
+ assertEquals(
+ Lists.newArrayList(
+ "void kotlin.jvm.internal.Intrinsics.throwParameterIsNullException(java.lang.String)"),
+ collectStaticCalls(clazz, "main", String[].class.getCanonicalName()));
+ });
}
- private Set<String> collectAccessedTypes(Predicate<DexType> isTypeOfInterest,
- ClassSubject clazz, String methodName, String... params) {
+ private Set<String> collectAccessedTypes(
+ Predicate<DexType> isTypeOfInterest,
+ ClassSubject clazz,
+ String methodName,
+ String... params) {
assertNotNull(clazz);
MethodSignature signature = new MethodSignature(methodName, "void", params);
DexCode code = clazz.method(signature).getMethod().getCode().asDexCode();
@@ -270,32 +304,37 @@
.collect(Collectors.toSet());
}
- protected void runTest(String folder, String mainClass,
- boolean enabled, AndroidAppInspector inspector) throws Exception {
- runTest(
+ private R8TestRunResult runTestWithDefaults(String folder, String mainClass) throws Exception {
+ return runTestWithDefaults(folder, mainClass, null);
+ }
+
+ private R8TestRunResult runTestWithDefaults(
+ String folder, String mainClass, ThrowableConsumer<R8FullTestBuilder> configuration)
+ throws Exception {
+ return runTest(
folder,
mainClass,
- // TODO(jsjeon): Introduce @NeverInline to kotlinR8TestResources
- StringUtils.lines(
- "-neverinline class * { void test*State*(...); }",
- "-neverinline class * { void testBigExtraMethod(...); }",
- "-neverinline class * { void testBigExtraMethodReturningLambda(...); }"),
- options -> {
- options.enableInlining = true;
- options.enableClassInlining = enabled;
- options.enableLambdaMerging = false;
+ testBuilder ->
+ testBuilder
+ .addOptionsModification(
+ options -> {
+ options.enableInlining = true;
+ options.enableLambdaMerging = false;
- // TODO(b/141719453): These limits should be removed if a possible or the test refactored.
- // Tests check if specific lambdas are inlined or not, where some of target lambdas have
- // at least 4 instructions.
- options.inliningInstructionLimit = 4;
- options.classInliningInstructionLimit = 40;
+ // TODO(b/141719453): These limits should be removed if a possible or the test
+ // refactored. Tests check if specific lambdas are inlined or not, where some
+ // of target lambdas have at least 4 instructions.
+ options.inliningInstructionLimit = 4;
+ options.classInliningInstructionLimit = 40;
- // Class inlining depends on the processing order. We therefore insert all call graph
- // edges and verify that we can class inline everything under this condition.
- options.testing.addCallEdgesForLibraryInvokes = true;
- },
- inspector);
+ // Class inlining depends on the processing order. We therefore insert all
+ // call graph edges and verify that we can class inline everything under this
+ // condition.
+ options.testing.addCallEdgesForLibraryInvokes = true;
+
+ options.enableHorizontalClassMergingOfKotlinLambdas = false;
+ })
+ .apply(configuration));
}
private List<String> collectStaticCalls(ClassSubject clazz, String methodName, String... params) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
index 2cbef3e..bf74d90 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
@@ -10,10 +10,10 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.google.common.base.Predicates;
import java.util.Collection;
@@ -40,48 +40,37 @@
final String mainClassName = "class_staticizer.MainKt";
// Without class staticizer.
- runTest(
- "class_staticizer",
- mainClassName,
- false,
- app -> {
- CodeInspector inspector = new CodeInspector(app);
- assertThat(inspector.clazz("class_staticizer.Derived$Companion"), isPresent());
+ runTest("class_staticizer", mainClassName, true)
+ .inspect(
+ inspector -> {
+ assertThat(inspector.clazz("class_staticizer.Derived$Companion"), isPresent());
- // The Util class is there, but its instance methods have been inlined.
- ClassSubject utilClass = inspector.clazz("class_staticizer.Util");
- assertThat(utilClass, isPresent());
- assertTrue(
- utilClass.allMethods().stream()
- .filter(Predicates.not(FoundMethodSubject::isStatic))
- .allMatch(FoundMethodSubject::isInstanceInitializer));
- });
+ // The Util class is there, but its instance methods have been inlined.
+ ClassSubject utilClass = inspector.clazz("class_staticizer.Util");
+ assertThat(utilClass, isPresent());
+ assertTrue(
+ utilClass.allMethods().stream()
+ .filter(Predicates.not(FoundMethodSubject::isStatic))
+ .allMatch(FoundMethodSubject::isInstanceInitializer));
+ });
// With class staticizer.
- runTest(
- "class_staticizer",
- mainClassName,
- true,
- app -> {
- CodeInspector inspector = new CodeInspector(app);
- assertThat(inspector.clazz("class_staticizer.Regular$Companion"), not(isPresent()));
+ runTest("class_staticizer", mainClassName, false)
+ .inspect(
+ inspector -> {
+ assertThat(inspector.clazz("class_staticizer.Regular$Companion"), not(isPresent()));
- ClassSubject utilClass = inspector.clazz("class_staticizer.Util");
- assertThat(utilClass, isPresent());
- assertTrue(utilClass.allMethods().stream().allMatch(FoundMethodSubject::isStatic));
- });
+ ClassSubject utilClass = inspector.clazz("class_staticizer.Util");
+ assertThat(utilClass, isPresent());
+ assertTrue(utilClass.allMethods().stream().allMatch(FoundMethodSubject::isStatic));
+ });
}
- protected void runTest(String folder, String mainClass,
- boolean enabled, AndroidAppInspector inspector) throws Exception {
- runTest(
+ protected R8TestRunResult runTest(String folder, String mainClass, boolean noClassStaticizing)
+ throws Exception {
+ return runTest(
folder,
mainClass,
- null,
- options -> {
- options.enableClassInlining = false;
- options.enableClassStaticizer = enabled;
- },
- inspector);
+ testBuilder -> testBuilder.noClassInlining().noClassStaticizing(noClassStaticizing));
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
index 782eb68..3c28a7d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
@@ -34,43 +33,37 @@
@Test
public void testMergingKStyleLambdasAfterUnusedArgumentRemoval() throws Exception {
final String mainClassName = "unused_arg_in_lambdas_kstyle.MainKt";
- runTest(
- "unused_arg_in_lambdas_kstyle",
- mainClassName,
- app -> {
- CodeInspector inspector = new CodeInspector(app);
- inspector.forAllClasses(
- classSubject -> {
- if (classSubject.getOriginalDescriptor().contains("$ks")) {
- MethodSubject init = classSubject.init(ImmutableList.of("int"));
- assertThat(init, isPresent());
- // Arity 2 should appear.
- assertTrue(init.iterateInstructions(i -> i.isConstNumber(2)).hasNext());
+ runTest("unused_arg_in_lambdas_kstyle", mainClassName)
+ .inspect(
+ inspector ->
+ inspector.forAllClasses(
+ classSubject -> {
+ if (classSubject.getOriginalDescriptor().contains("$ks")) {
+ MethodSubject init = classSubject.init(ImmutableList.of("int"));
+ assertThat(init, isPresent());
+ // Arity 2 should appear.
+ assertTrue(init.iterateInstructions(i -> i.isConstNumber(2)).hasNext());
- MethodSubject invoke = classSubject.uniqueMethodWithName("invoke");
- assertThat(invoke, isPresent());
- assertEquals(2, invoke.getMethod().method.proto.parameters.size());
- }
- });
- });
+ MethodSubject invoke = classSubject.uniqueMethodWithName("invoke");
+ assertThat(invoke, isPresent());
+ assertEquals(2, invoke.getMethod().method.proto.parameters.size());
+ }
+ }));
}
@Test
public void testMergingJStyleLambdasAfterUnusedArgumentRemoval() throws Exception {
final String mainClassName = "unused_arg_in_lambdas_jstyle.MainKt";
- runTest(
- "unused_arg_in_lambdas_jstyle",
- mainClassName,
- app -> {
- CodeInspector inspector = new CodeInspector(app);
- inspector.forAllClasses(
- classSubject -> {
- if (classSubject.getOriginalDescriptor().contains("$js")) {
- MethodSubject get = classSubject.uniqueMethodWithName("get");
- assertThat(get, isPresent());
- assertEquals(3, get.getMethod().method.proto.parameters.size());
- }
- });
- });
+ runTest("unused_arg_in_lambdas_jstyle", mainClassName)
+ .inspect(
+ inspector ->
+ inspector.forAllClasses(
+ classSubject -> {
+ if (classSubject.getOriginalDescriptor().contains("$js")) {
+ MethodSubject get = classSubject.uniqueMethodWithName("get");
+ assertThat(get, isPresent());
+ assertEquals(3, get.getMethod().method.proto.parameters.size());
+ }
+ }));
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java
index c82a4a4..10fe60e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FieldSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
@@ -43,53 +42,51 @@
public void b110196118() throws Exception {
final String mainClassName = "unused_singleton.MainKt";
final String moduleName = "unused_singleton.TestModule";
- runTest(
- "unused_singleton",
- mainClassName,
- "-dontobfuscate",
- app -> {
- CodeInspector inspector = new CodeInspector(app);
- ClassSubject main = inspector.clazz(mainClassName);
- assertThat(main, isPresent());
+ runTest("unused_singleton", mainClassName)
+ .inspect(
+ inspector -> {
+ ClassSubject main = inspector.clazz(mainClassName);
+ assertThat(main, isPresent());
- MethodSubject mainMethod = main.mainMethod();
- assertThat(mainMethod, isPresent());
+ MethodSubject mainMethod = main.mainMethod();
+ assertThat(mainMethod, isPresent());
- // The const-string from provideGreeting() has been propagated.
- assertTrue(
- mainMethod
- .iterateInstructions(i -> i.isConstString("Hello", JumboStringMode.ALLOW))
- .hasNext());
+ // The const-string from provideGreeting() has been propagated.
+ assertTrue(
+ mainMethod
+ .iterateInstructions(i -> i.isConstString("Hello", JumboStringMode.ALLOW))
+ .hasNext());
- // The method provideGreeting() is no longer being invoked -- i.e., we have been able to
- // determine that the class initialization of the enclosing class is trivial.
- ClassSubject module = inspector.clazz(moduleName);
- assertThat(main, isPresent());
- assertEquals(
- 0,
- mainMethod
- .streamInstructions()
- .filter(InstructionSubject::isInvoke)
- .map(i -> i.getMethod().toSourceString())
- .filter(
- invokedMethod ->
- !invokedMethod.equals(checkParameterIsNotNullSignature)
- && !invokedMethod.equals(printlnSignature)
- && !invokedMethod.equals(throwParameterIsNotNullExceptionSignature))
- .count());
+ // The method provideGreeting() is no longer being invoked -- i.e., we have been able
+ // to determine that the class initialization of the enclosing class is trivial.
+ ClassSubject module = inspector.clazz(moduleName);
+ assertThat(main, isPresent());
+ assertEquals(
+ 0,
+ mainMethod
+ .streamInstructions()
+ .filter(InstructionSubject::isInvoke)
+ .map(i -> i.getMethod().toSourceString())
+ .filter(
+ invokedMethod ->
+ !invokedMethod.equals(checkParameterIsNotNullSignature)
+ && !invokedMethod.equals(printlnSignature)
+ && !invokedMethod.equals(
+ throwParameterIsNotNullExceptionSignature))
+ .count());
- // The field `INSTANCE` has been removed entirely.
- FieldSubject instance = module.uniqueFieldWithName("INSTANCE");
- assertThat(instance, not(isPresent()));
+ // The field `INSTANCE` has been removed entirely.
+ FieldSubject instance = module.uniqueFieldWithName("INSTANCE");
+ assertThat(instance, not(isPresent()));
- // The class initializer is no longer there.
- MethodSubject clinit = module.clinit();
- assertThat(clinit, not(isPresent()));
+ // The class initializer is no longer there.
+ MethodSubject clinit = module.clinit();
+ assertThat(clinit, not(isPresent()));
- // Also, the instance initializer is no longer there, since it is only reachable from the
- // class initializer.
- MethodSubject init = module.init(ImmutableList.of());
- assertThat(init, not(isPresent()));
- });
+ // Also, the instance initializer is no longer there, since it is only reachable from
+ // the class initializer.
+ MethodSubject init = module.init(ImmutableList.of());
+ assertThat(init, not(isPresent()));
+ });
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
index e5ce02c..b4aeced 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -8,6 +8,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.ToolHelper.ProcessResult;
@@ -18,14 +19,11 @@
import com.android.tools.r8.naming.MemberNaming;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FieldSubject;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
-import java.util.function.Consumer;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
@@ -67,9 +65,6 @@
.addProperty("property", JAVA_LANG_STRING, Visibility.PRIVATE)
.addProperty("indirectPropertyGetter", JAVA_LANG_STRING, Visibility.PRIVATE);
- private Consumer<InternalOptions> disableClassStaticizer =
- opts -> opts.enableClassStaticizer = false;
-
@Parameterized.Parameters(name = "target: {0}, allowAccessModification: {1}")
public static Collection<Object[]> data() {
return buildParameters(KotlinTargetVersion.values(), BooleanUtils.values());
@@ -85,33 +80,32 @@
final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionPropertiesKt",
"companionProperties_usePrimitiveProp");
- runTest(
- PROPERTIES_PACKAGE_NAME,
- mainClass,
- disableClassStaticizer,
- (app) -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject outerClass =
- checkClassIsKept(codeInspector, testedClass.getOuterClassName());
- String propertyName = "primitiveProp";
- FieldSubject fieldSubject = checkFieldIsKept(outerClass, "int", propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ runTest(PROPERTIES_PACKAGE_NAME, mainClass, R8TestBuilder::noClassStaticizing)
+ .inspect(
+ inspector -> {
+ ClassSubject outerClass =
+ checkClassIsKept(inspector, testedClass.getOuterClassName());
+ String propertyName = "primitiveProp";
+ FieldSubject fieldSubject = checkFieldIsKept(outerClass, "int", propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getterAccessor =
- testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
- MemberNaming.MethodSignature setterAccessor =
- testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(
+ propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature setterAccessor =
+ testedClass.getSetterAccessorForProperty(
+ propertyName, AccessorKind.FROM_COMPANION);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- checkMethodIsRemoved(outerClass, getterAccessor);
- checkMethodIsRemoved(outerClass, setterAccessor);
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- checkMethodIsKept(outerClass, getterAccessor);
- checkMethodIsKept(outerClass, setterAccessor);
- }
- });
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsRemoved(outerClass, getterAccessor);
+ checkMethodIsRemoved(outerClass, setterAccessor);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsKept(outerClass, getterAccessor);
+ checkMethodIsKept(outerClass, setterAccessor);
+ }
+ });
}
@Test
@@ -119,34 +113,34 @@
final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionPropertiesKt",
"companionProperties_usePrivateProp");
- runTest(
- PROPERTIES_PACKAGE_NAME,
- mainClass,
- disableClassStaticizer,
- (app) -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject outerClass =
- checkClassIsKept(codeInspector, testedClass.getOuterClassName());
- String propertyName = "privateProp";
- FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ runTest(PROPERTIES_PACKAGE_NAME, mainClass, R8TestBuilder::noClassStaticizing)
+ .inspect(
+ inspector -> {
+ ClassSubject outerClass =
+ checkClassIsKept(inspector, testedClass.getOuterClassName());
+ String propertyName = "privateProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getterAccessor =
- testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
- MemberNaming.MethodSignature setterAccessor =
- testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(
+ propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature setterAccessor =
+ testedClass.getSetterAccessorForProperty(
+ propertyName, AccessorKind.FROM_COMPANION);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
- checkMethodIsRemoved(outerClass, getterAccessor);
- checkMethodIsRemoved(outerClass, setterAccessor);
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsRemoved(outerClass, getterAccessor);
+ checkMethodIsRemoved(outerClass, setterAccessor);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- checkMethodIsKept(outerClass, getterAccessor);
- checkMethodIsKept(outerClass, setterAccessor);
- }
- });
+ checkMethodIsKept(outerClass, getterAccessor);
+ checkMethodIsKept(outerClass, setterAccessor);
+ }
+ });
}
@Test
@@ -154,33 +148,33 @@
final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionPropertiesKt",
"companionProperties_useInternalProp");
- runTest(
- PROPERTIES_PACKAGE_NAME,
- mainClass,
- disableClassStaticizer,
- (app) -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject outerClass =
- checkClassIsKept(codeInspector, testedClass.getOuterClassName());
- String propertyName = "internalProp";
- FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ runTest(PROPERTIES_PACKAGE_NAME, mainClass, R8TestBuilder::noClassStaticizing)
+ .inspect(
+ inspector -> {
+ ClassSubject outerClass =
+ checkClassIsKept(inspector, testedClass.getOuterClassName());
+ String propertyName = "internalProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getterAccessor =
- testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
- MemberNaming.MethodSignature setterAccessor =
- testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(
+ propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature setterAccessor =
+ testedClass.getSetterAccessorForProperty(
+ propertyName, AccessorKind.FROM_COMPANION);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- checkMethodIsRemoved(outerClass, getterAccessor);
- checkMethodIsRemoved(outerClass, setterAccessor);
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- checkMethodIsKept(outerClass, getterAccessor);
- checkMethodIsKept(outerClass, setterAccessor);
- }
- });
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsRemoved(outerClass, getterAccessor);
+ checkMethodIsRemoved(outerClass, setterAccessor);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsKept(outerClass, getterAccessor);
+ checkMethodIsKept(outerClass, setterAccessor);
+ }
+ });
}
@Test
@@ -188,33 +182,33 @@
final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionPropertiesKt",
"companionProperties_usePublicProp");
- runTest(
- PROPERTIES_PACKAGE_NAME,
- mainClass,
- disableClassStaticizer,
- (app) -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject outerClass =
- checkClassIsKept(codeInspector, testedClass.getOuterClassName());
- String propertyName = "publicProp";
- FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ runTest(PROPERTIES_PACKAGE_NAME, mainClass, R8TestBuilder::noClassStaticizing)
+ .inspect(
+ inspector -> {
+ ClassSubject outerClass =
+ checkClassIsKept(inspector, testedClass.getOuterClassName());
+ String propertyName = "publicProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getterAccessor =
- testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
- MemberNaming.MethodSignature setterAccessor =
- testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(
+ propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature setterAccessor =
+ testedClass.getSetterAccessorForProperty(
+ propertyName, AccessorKind.FROM_COMPANION);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- checkMethodIsRemoved(outerClass, getterAccessor);
- checkMethodIsRemoved(outerClass, setterAccessor);
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- checkMethodIsKept(outerClass, getterAccessor);
- checkMethodIsKept(outerClass, setterAccessor);
- }
- });
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsRemoved(outerClass, getterAccessor);
+ checkMethodIsRemoved(outerClass, setterAccessor);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsKept(outerClass, getterAccessor);
+ checkMethodIsKept(outerClass, setterAccessor);
+ }
+ });
}
@Test
@@ -222,32 +216,32 @@
final TestKotlinCompanionClass testedClass = COMPANION_LATE_INIT_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
"companionLateInitProperties_usePrivateLateInitProp");
- runTest(
- PROPERTIES_PACKAGE_NAME,
- mainClass,
- disableClassStaticizer,
- (app) -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject outerClass =
- checkClassIsKept(codeInspector, testedClass.getOuterClassName());
- String propertyName = "privateLateInitProp";
- FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ runTest(PROPERTIES_PACKAGE_NAME, mainClass, R8TestBuilder::noClassStaticizing)
+ .inspect(
+ inspector -> {
+ ClassSubject outerClass =
+ checkClassIsKept(inspector, testedClass.getOuterClassName());
+ String propertyName = "privateLateInitProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getterAccessor =
- testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
- MemberNaming.MethodSignature setterAccessor =
- testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- checkMethodIsRemoved(outerClass, getterAccessor);
- checkMethodIsRemoved(outerClass, setterAccessor);
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- checkMethodIsKept(outerClass, getterAccessor);
- checkMethodIsKept(outerClass, setterAccessor);
- }
- });
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(
+ propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature setterAccessor =
+ testedClass.getSetterAccessorForProperty(
+ propertyName, AccessorKind.FROM_COMPANION);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsRemoved(outerClass, getterAccessor);
+ checkMethodIsRemoved(outerClass, setterAccessor);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsKept(outerClass, getterAccessor);
+ checkMethodIsKept(outerClass, setterAccessor);
+ }
+ });
}
@Test
@@ -255,23 +249,28 @@
final TestKotlinCompanionClass testedClass = COMPANION_LATE_INIT_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
"companionLateInitProperties_useInternalLateInitProp");
- runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject outerClass = checkClassIsKept(codeInspector, testedClass.getOuterClassName());
- String propertyName = "internalLateInitProp";
- FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ runTest(PROPERTIES_PACKAGE_NAME, mainClass)
+ .inspect(
+ inspector -> {
+ ClassSubject outerClass =
+ checkClassIsKept(inspector, testedClass.getOuterClassName());
+ String propertyName = "internalLateInitProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
- MemberNaming.MethodSignature getterAccessor = testedClass
- .getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
- MemberNaming.MethodSignature setterAccessor = testedClass
- .getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(
+ propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature setterAccessor =
+ testedClass.getSetterAccessorForProperty(
+ propertyName, AccessorKind.FROM_COMPANION);
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- checkMethodIsRemoved(outerClass, getterAccessor);
- checkMethodIsRemoved(outerClass, setterAccessor);
- });
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsRemoved(outerClass, getterAccessor);
+ checkMethodIsRemoved(outerClass, setterAccessor);
+ });
}
@Test
@@ -279,23 +278,28 @@
final TestKotlinCompanionClass testedClass = COMPANION_LATE_INIT_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
"companionLateInitProperties_usePublicLateInitProp");
- runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject outerClass = checkClassIsKept(codeInspector, testedClass.getOuterClassName());
- String propertyName = "publicLateInitProp";
- FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ runTest(PROPERTIES_PACKAGE_NAME, mainClass)
+ .inspect(
+ inspector -> {
+ ClassSubject outerClass =
+ checkClassIsKept(inspector, testedClass.getOuterClassName());
+ String propertyName = "publicLateInitProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
- MemberNaming.MethodSignature getterAccessor = testedClass
- .getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
- MemberNaming.MethodSignature setterAccessor = testedClass
- .getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(
+ propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature setterAccessor =
+ testedClass.getSetterAccessorForProperty(
+ propertyName, AccessorKind.FROM_COMPANION);
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- checkMethodIsRemoved(outerClass, getterAccessor);
- checkMethodIsRemoved(outerClass, setterAccessor);
- });
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsRemoved(outerClass, getterAccessor);
+ checkMethodIsRemoved(outerClass, setterAccessor);
+ });
}
@Test
@@ -303,17 +307,14 @@
TestKotlinCompanionClass testedClass = ACCESSOR_COMPANION_PROPERTY_CLASS;
String mainClass =
addMainToClasspath("accessors.AccessorKt", "accessor_accessPropertyFromCompanionClass");
- runTest(
- "accessors",
- mainClass,
- disableClassStaticizer,
- app -> {
- // The classes are removed entirely as a result of member value propagation, inlining, and
- // the fact that the classes do not have observable side effects.
- CodeInspector codeInspector = new CodeInspector(app);
- checkClassIsRemoved(codeInspector, testedClass.getOuterClassName());
- checkClassIsRemoved(codeInspector, testedClass.getClassName());
- });
+ runTest("accessors", mainClass, R8TestBuilder::noClassStaticizing)
+ .inspect(
+ inspector -> {
+ // The classes are removed entirely as a result of member value propagation, inlining,
+ // and the fact that the classes do not have observable side effects.
+ checkClassIsRemoved(inspector, testedClass.getOuterClassName());
+ checkClassIsRemoved(inspector, testedClass.getClassName());
+ });
}
@Test
@@ -322,29 +323,32 @@
TestKotlinCompanionClass testedClass = ACCESSOR_COMPANION_PROPERTY_CLASS;
String mainClass = addMainToClasspath("accessors.AccessorKt",
"accessor_accessPropertyFromOuterClass");
- runTest("accessors", mainClass, (app) -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject outerClass = checkClassIsKept(codeInspector, testedClass.getOuterClassName());
- ClassSubject companionClass = checkClassIsKept(codeInspector, testedClass.getClassName());
- String propertyName = "property";
- FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ runTest("accessors", mainClass)
+ .inspect(
+ inspector -> {
+ ClassSubject outerClass =
+ checkClassIsKept(inspector, testedClass.getOuterClassName());
+ ClassSubject companionClass = checkClassIsKept(inspector, testedClass.getClassName());
+ String propertyName = "property";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
- // We cannot inline the getter because we don't know if NPE is preserved.
- MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
- checkMethodIsKept(companionClass, getter);
+ // We cannot inline the getter because we don't know if NPE is preserved.
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
+ checkMethodIsKept(companionClass, getter);
- // We should always inline the static accessor method.
- MemberNaming.MethodSignature getterAccessor =
- testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
- checkMethodIsRemoved(companionClass, getterAccessor);
+ // We should always inline the static accessor method.
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
+ checkMethodIsRemoved(companionClass, getterAccessor);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- }
- });
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ }
+ });
}
@Test
@@ -352,15 +356,12 @@
String mainClass =
addMainToClasspath(
"accessors.PropertyAccessorForInnerClassKt", "noUseOfPropertyAccessorFromInnerClass");
- runTest(
- "accessors",
- mainClass,
- (app) -> {
- CodeInspector codeInspector = new CodeInspector(app);
-
- // Class is removed because the instantiation of the inner class has no side effects.
- checkClassIsRemoved(codeInspector, PROPERTY_ACCESS_FOR_INNER_CLASS.getClassName());
- });
+ runTest("accessors", mainClass)
+ .inspect(
+ inspector -> {
+ // Class is removed because the instantiation of the inner class has no side effects.
+ checkClassIsRemoved(inspector, PROPERTY_ACCESS_FOR_INNER_CLASS.getClassName());
+ });
}
@Test
@@ -369,29 +370,30 @@
TestKotlinClass testedClass = PROPERTY_ACCESS_FOR_INNER_CLASS;
String mainClass = addMainToClasspath(testedClass.className + "Kt",
"usePrivatePropertyAccessorFromInnerClass");
- runTest("accessors", mainClass, (app) -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject classSubject = checkClassIsKept(codeInspector, testedClass.getClassName());
+ runTest("accessors", mainClass)
+ .inspect(
+ inspector -> {
+ ClassSubject classSubject = checkClassIsKept(inspector, testedClass.getClassName());
- String propertyName = "privateProp";
- FieldSubject fieldSubject = checkFieldIsKept(classSubject, JAVA_LANG_STRING,
- propertyName);
- assertFalse(fieldSubject.getField().accessFlags.isStatic());
+ String propertyName = "privateProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(classSubject, JAVA_LANG_STRING, propertyName);
+ assertFalse(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getterAccessor =
- testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
- MemberNaming.MethodSignature setterAccessor =
- testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- checkMethodIsRemoved(classSubject, getterAccessor);
- checkMethodIsRemoved(classSubject, setterAccessor);
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- checkMethodIsKept(classSubject, getterAccessor);
- checkMethodIsKept(classSubject, setterAccessor);
- }
- });
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
+ MemberNaming.MethodSignature setterAccessor =
+ testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsRemoved(classSubject, getterAccessor);
+ checkMethodIsRemoved(classSubject, setterAccessor);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsKept(classSubject, getterAccessor);
+ checkMethodIsKept(classSubject, setterAccessor);
+ }
+ });
}
@Test
@@ -400,29 +402,30 @@
TestKotlinClass testedClass = PROPERTY_ACCESS_FOR_INNER_CLASS;
String mainClass = addMainToClasspath(testedClass.className + "Kt",
"usePrivateLateInitPropertyAccessorFromInnerClass");
- runTest("accessors", mainClass, (app) -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject classSubject = checkClassIsKept(codeInspector, testedClass.getClassName());
+ runTest("accessors", mainClass)
+ .inspect(
+ inspector -> {
+ ClassSubject classSubject = checkClassIsKept(inspector, testedClass.getClassName());
- String propertyName = "privateLateInitProp";
- FieldSubject fieldSubject = checkFieldIsKept(classSubject, JAVA_LANG_STRING,
- propertyName);
- assertFalse(fieldSubject.getField().accessFlags.isStatic());
+ String propertyName = "privateLateInitProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(classSubject, JAVA_LANG_STRING, propertyName);
+ assertFalse(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getterAccessor =
- testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
- MemberNaming.MethodSignature setterAccessor =
- testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- checkMethodIsRemoved(classSubject, getterAccessor);
- checkMethodIsRemoved(classSubject, setterAccessor);
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- checkMethodIsKept(classSubject, getterAccessor);
- checkMethodIsKept(classSubject, setterAccessor);
- }
- });
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
+ MemberNaming.MethodSignature setterAccessor =
+ testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsRemoved(classSubject, getterAccessor);
+ checkMethodIsRemoved(classSubject, setterAccessor);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsKept(classSubject, getterAccessor);
+ checkMethodIsKept(classSubject, setterAccessor);
+ }
+ });
}
@Test
@@ -431,19 +434,20 @@
TestKotlinClass testedClass = PROPERTY_ACCESS_FOR_LAMBDA_CLASS;
String mainClass = addMainToClasspath(testedClass.className + "Kt",
"noUseOfPropertyAccessorFromLambda");
- runTest("accessors", mainClass, (app) -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject classSubject = checkClassIsKept(codeInspector, testedClass.getClassName());
- String propertyName = "property";
+ runTest("accessors", mainClass)
+ .inspect(
+ inspector -> {
+ ClassSubject classSubject = checkClassIsKept(inspector, testedClass.getClassName());
+ String propertyName = "property";
- MemberNaming.MethodSignature getterAccessor =
- testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_LAMBDA);
- MemberNaming.MethodSignature setterAccessor =
- testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_LAMBDA);
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_LAMBDA);
+ MemberNaming.MethodSignature setterAccessor =
+ testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_LAMBDA);
- checkMethodIsRemoved(classSubject, getterAccessor);
- checkMethodIsRemoved(classSubject, setterAccessor);
- });
+ checkMethodIsRemoved(classSubject, getterAccessor);
+ checkMethodIsRemoved(classSubject, setterAccessor);
+ });
}
@Test
@@ -452,27 +456,29 @@
TestKotlinClass testedClass = PROPERTY_ACCESS_FOR_LAMBDA_CLASS;
String mainClass = addMainToClasspath(testedClass.className + "Kt",
"usePropertyAccessorFromLambda");
- runTest("accessors", mainClass, (app) -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject classSubject = checkClassIsKept(codeInspector, testedClass.getClassName());
- String propertyName = "property";
- FieldSubject fieldSubject = checkFieldIsKept(classSubject, JAVA_LANG_STRING, propertyName);
- assertFalse(fieldSubject.getField().accessFlags.isStatic());
+ runTest("accessors", mainClass)
+ .inspect(
+ inspector -> {
+ ClassSubject classSubject = checkClassIsKept(inspector, testedClass.getClassName());
+ String propertyName = "property";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(classSubject, JAVA_LANG_STRING, propertyName);
+ assertFalse(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getterAccessor =
- testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_LAMBDA);
- MemberNaming.MethodSignature setterAccessor =
- testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_LAMBDA);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- checkMethodIsRemoved(classSubject, getterAccessor);
- checkMethodIsRemoved(classSubject, setterAccessor);
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- checkMethodIsKept(classSubject, getterAccessor);
- checkMethodIsKept(classSubject, setterAccessor);
- }
- });
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_LAMBDA);
+ MemberNaming.MethodSignature setterAccessor =
+ testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_LAMBDA);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsRemoved(classSubject, getterAccessor);
+ checkMethodIsRemoved(classSubject, setterAccessor);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsKept(classSubject, getterAccessor);
+ checkMethodIsKept(classSubject, setterAccessor);
+ }
+ });
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
index 59c92ff..ec0ee03 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.util.Collection;
import java.util.Collections;
@@ -59,33 +58,39 @@
final MethodSignature testMethodSignature =
new MethodSignature("testDataClassGetters", "void", Collections.emptyList());
final String extraRules = keepClassMethod(mainClassName, testMethodSignature);
- runTest("dataclass", mainClassName, extraRules, disableClassInliner, (app) -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject dataClass = checkClassIsKept(codeInspector, TEST_DATA_CLASS.getClassName());
+ runTest(
+ "dataclass",
+ mainClassName,
+ testBuilder ->
+ testBuilder.addKeepRules(extraRules).addOptionsModification(disableClassInliner))
+ .inspect(
+ inspector -> {
+ ClassSubject dataClass = checkClassIsKept(inspector, TEST_DATA_CLASS.getClassName());
- // Getters should be removed after inlining, which is possible only if access is relaxed.
- final boolean areGetterPresent = !allowAccessModification;
- checkMethodIsKeptOrRemoved(dataClass, NAME_GETTER_METHOD, areGetterPresent);
- checkMethodIsKeptOrRemoved(dataClass, AGE_GETTER_METHOD, areGetterPresent);
+ // Getters should be removed after inlining, which is possible only if access is
+ // relaxed.
+ final boolean areGetterPresent = !allowAccessModification;
+ checkMethodIsKeptOrRemoved(dataClass, NAME_GETTER_METHOD, areGetterPresent);
+ checkMethodIsKeptOrRemoved(dataClass, AGE_GETTER_METHOD, areGetterPresent);
- // No use of componentN functions.
- checkMethodIsRemoved(dataClass, COMPONENT1_METHOD);
- checkMethodIsRemoved(dataClass, COMPONENT2_METHOD);
+ // No use of componentN functions.
+ checkMethodIsRemoved(dataClass, COMPONENT1_METHOD);
+ checkMethodIsRemoved(dataClass, COMPONENT2_METHOD);
- // No use of copy functions.
- checkMethodIsRemoved(dataClass, COPY_METHOD);
- checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
+ // No use of copy functions.
+ checkMethodIsRemoved(dataClass, COPY_METHOD);
+ checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
- ClassSubject classSubject = checkClassIsKept(codeInspector, mainClassName);
- MethodSubject testMethod = checkMethodIsKept(classSubject, testMethodSignature);
- DexCode dexCode = getDexCode(testMethod);
- if (allowAccessModification) {
- // Both getters should be inlined
- checkMethodIsNeverInvoked(dexCode, NAME_GETTER_METHOD, AGE_GETTER_METHOD);
- } else {
- checkMethodIsInvokedAtLeastOnce(dexCode, NAME_GETTER_METHOD, AGE_GETTER_METHOD);
- }
- });
+ ClassSubject classSubject = checkClassIsKept(inspector, mainClassName);
+ MethodSubject testMethod = checkMethodIsKept(classSubject, testMethodSignature);
+ DexCode dexCode = getDexCode(testMethod);
+ if (allowAccessModification) {
+ // Both getters should be inlined
+ checkMethodIsNeverInvoked(dexCode, NAME_GETTER_METHOD, AGE_GETTER_METHOD);
+ } else {
+ checkMethodIsInvokedAtLeastOnce(dexCode, NAME_GETTER_METHOD, AGE_GETTER_METHOD);
+ }
+ });
}
@Test
@@ -94,33 +99,38 @@
final MethodSignature testMethodSignature =
new MethodSignature("testAllDataClassComponentFunctions", "void", Collections.emptyList());
final String extraRules = keepClassMethod(mainClassName, testMethodSignature);
- runTest("dataclass", mainClassName, extraRules, disableClassInliner, (app) -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject dataClass = checkClassIsKept(codeInspector, TEST_DATA_CLASS.getClassName());
+ runTest(
+ "dataclass",
+ mainClassName,
+ testBuilder ->
+ testBuilder.addKeepRules(extraRules).addOptionsModification(disableClassInliner))
+ .inspect(
+ inspector -> {
+ ClassSubject dataClass = checkClassIsKept(inspector, TEST_DATA_CLASS.getClassName());
- // ComponentN functions should be removed after inlining, which is possible only if access
- // is relaxed.
- final boolean areComponentMethodsPresent = !allowAccessModification;
- checkMethodIsKeptOrRemoved(dataClass, COMPONENT1_METHOD, areComponentMethodsPresent);
- checkMethodIsKeptOrRemoved(dataClass, COMPONENT2_METHOD, areComponentMethodsPresent);
+ // ComponentN functions should be removed after inlining, which is possible only if
+ // access is relaxed.
+ final boolean areComponentMethodsPresent = !allowAccessModification;
+ checkMethodIsKeptOrRemoved(dataClass, COMPONENT1_METHOD, areComponentMethodsPresent);
+ checkMethodIsKeptOrRemoved(dataClass, COMPONENT2_METHOD, areComponentMethodsPresent);
- // No use of getter.
- checkMethodIsRemoved(dataClass, NAME_GETTER_METHOD);
- checkMethodIsRemoved(dataClass, AGE_GETTER_METHOD);
+ // No use of getter.
+ checkMethodIsRemoved(dataClass, NAME_GETTER_METHOD);
+ checkMethodIsRemoved(dataClass, AGE_GETTER_METHOD);
- // No use of copy functions.
- checkMethodIsRemoved(dataClass, COPY_METHOD);
- checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
+ // No use of copy functions.
+ checkMethodIsRemoved(dataClass, COPY_METHOD);
+ checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
- ClassSubject classSubject = checkClassIsKept(codeInspector, mainClassName);
- MethodSubject testMethod = checkMethodIsKept(classSubject, testMethodSignature);
- DexCode dexCode = getDexCode(testMethod);
- if (allowAccessModification) {
- checkMethodIsNeverInvoked(dexCode, COMPONENT1_METHOD, COMPONENT2_METHOD);
- } else {
- checkMethodIsInvokedAtLeastOnce(dexCode, COMPONENT1_METHOD, COMPONENT2_METHOD);
- }
- });
+ ClassSubject classSubject = checkClassIsKept(inspector, mainClassName);
+ MethodSubject testMethod = checkMethodIsKept(classSubject, testMethodSignature);
+ DexCode dexCode = getDexCode(testMethod);
+ if (allowAccessModification) {
+ checkMethodIsNeverInvoked(dexCode, COMPONENT1_METHOD, COMPONENT2_METHOD);
+ } else {
+ checkMethodIsInvokedAtLeastOnce(dexCode, COMPONENT1_METHOD, COMPONENT2_METHOD);
+ }
+ });
}
@Test
@@ -129,33 +139,38 @@
final MethodSignature testMethodSignature =
new MethodSignature("testSomeDataClassComponentFunctions", "void", Collections.emptyList());
final String extraRules = keepClassMethod(mainClassName, testMethodSignature);
- runTest("dataclass", mainClassName, extraRules, disableClassInliner, (app) -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject dataClass = checkClassIsKept(codeInspector, TEST_DATA_CLASS.getClassName());
+ runTest(
+ "dataclass",
+ mainClassName,
+ testBuilder ->
+ testBuilder.addKeepRules(extraRules).addOptionsModification(disableClassInliner))
+ .inspect(
+ inspector -> {
+ ClassSubject dataClass = checkClassIsKept(inspector, TEST_DATA_CLASS.getClassName());
- boolean component2IsPresent = !allowAccessModification;
- checkMethodIsKeptOrRemoved(dataClass, COMPONENT2_METHOD, component2IsPresent);
+ boolean component2IsPresent = !allowAccessModification;
+ checkMethodIsKeptOrRemoved(dataClass, COMPONENT2_METHOD, component2IsPresent);
- // Function component1 is not used.
- checkMethodIsRemoved(dataClass, COMPONENT1_METHOD);
+ // Function component1 is not used.
+ checkMethodIsRemoved(dataClass, COMPONENT1_METHOD);
- // No use of getter.
- checkMethodIsRemoved(dataClass, NAME_GETTER_METHOD);
- checkMethodIsRemoved(dataClass, AGE_GETTER_METHOD);
+ // No use of getter.
+ checkMethodIsRemoved(dataClass, NAME_GETTER_METHOD);
+ checkMethodIsRemoved(dataClass, AGE_GETTER_METHOD);
- // No use of copy functions.
- checkMethodIsRemoved(dataClass, COPY_METHOD);
- checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
+ // No use of copy functions.
+ checkMethodIsRemoved(dataClass, COPY_METHOD);
+ checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
- ClassSubject classSubject = checkClassIsKept(codeInspector, mainClassName);
- MethodSubject testMethod = checkMethodIsKept(classSubject, testMethodSignature);
- DexCode dexCode = getDexCode(testMethod);
- if (allowAccessModification) {
- checkMethodIsNeverInvoked(dexCode, COMPONENT2_METHOD);
- } else {
- checkMethodIsInvokedAtLeastOnce(dexCode, COMPONENT2_METHOD);
- }
- });
+ ClassSubject classSubject = checkClassIsKept(inspector, mainClassName);
+ MethodSubject testMethod = checkMethodIsKept(classSubject, testMethodSignature);
+ DexCode dexCode = getDexCode(testMethod);
+ if (allowAccessModification) {
+ checkMethodIsNeverInvoked(dexCode, COMPONENT2_METHOD);
+ } else {
+ checkMethodIsInvokedAtLeastOnce(dexCode, COMPONENT2_METHOD);
+ }
+ });
}
@Test
@@ -164,13 +179,18 @@
final MethodSignature testMethodSignature =
new MethodSignature("testDataClassCopy", "void", Collections.emptyList());
final String extraRules = keepClassMethod(mainClassName, testMethodSignature);
- runTest("dataclass", mainClassName, extraRules, disableClassInliner, (app) -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject dataClass = checkClassIsKept(codeInspector, TEST_DATA_CLASS.getClassName());
+ runTest(
+ "dataclass",
+ mainClassName,
+ testBuilder ->
+ testBuilder.addKeepRules(extraRules).addOptionsModification(disableClassInliner))
+ .inspect(
+ inspector -> {
+ ClassSubject dataClass = checkClassIsKept(inspector, TEST_DATA_CLASS.getClassName());
- checkMethodIsRemoved(dataClass, COPY_METHOD);
- checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
- });
+ checkMethodIsRemoved(dataClass, COPY_METHOD);
+ checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
+ });
}
@Test
@@ -179,12 +199,17 @@
final MethodSignature testMethodSignature =
new MethodSignature("testDataClassCopyWithDefault", "void", Collections.emptyList());
final String extraRules = keepClassMethod(mainClassName, testMethodSignature);
- runTest("dataclass", mainClassName, extraRules, disableClassInliner, (app) -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject dataClass = checkClassIsKept(codeInspector, TEST_DATA_CLASS.getClassName());
+ runTest(
+ "dataclass",
+ mainClassName,
+ testBuilder ->
+ testBuilder.addKeepRules(extraRules).addOptionsModification(disableClassInliner))
+ .inspect(
+ inspector -> {
+ ClassSubject dataClass = checkClassIsKept(inspector, TEST_DATA_CLASS.getClassName());
- checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
- });
+ checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
+ });
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
index 7a9635e..770d6cc 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
@@ -8,11 +8,11 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import java.util.Collection;
import java.util.Collections;
+import kotlin.jvm.internal.Intrinsics;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -39,20 +39,31 @@
new MethodSignature("expectsNonNullParameters",
"java.lang.String", Lists.newArrayList("java.lang.String", "java.lang.String")));
- runTest("intrinsics", "intrinsics.IntrinsicsKt", extraRules, (app) -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject intrinsicsClass = checkClassIsKept(
- codeInspector, KOTLIN_INTRINSICS_CLASS.getClassName());
-
- checkMethodsPresence(intrinsicsClass,
- ImmutableMap.<MethodSignature, Boolean>builder()
- .put(new MethodSignature("throwParameterIsNullException",
- "void", Collections.singletonList("java.lang.String")),
- true)
- .put(new MethodSignature("checkParameterIsNotNull",
- "void", Lists.newArrayList("java.lang.Object", "java.lang.String")),
- allowAccessModification ? false /* should be inlined*/ : true)
- .build());
- });
+ runTest(
+ "intrinsics",
+ "intrinsics.IntrinsicsKt",
+ testBuilder ->
+ testBuilder.addKeepRules(extraRules).noHorizontalClassMerging(Intrinsics.class))
+ .inspect(
+ inspector -> {
+ ClassSubject intrinsicsClass =
+ checkClassIsKept(inspector, KOTLIN_INTRINSICS_CLASS.getClassName());
+ checkMethodsPresence(
+ intrinsicsClass,
+ ImmutableMap.<MethodSignature, Boolean>builder()
+ .put(
+ new MethodSignature(
+ "throwParameterIsNullException",
+ "void",
+ Collections.singletonList("java.lang.String")),
+ true)
+ .put(
+ new MethodSignature(
+ "checkParameterIsNotNull",
+ "void",
+ Lists.newArrayList("java.lang.Object", "java.lang.String")),
+ !allowAccessModification)
+ .build());
+ });
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
index f572789..30511bb 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FieldSubject;
import java.util.Collection;
import java.util.function.Consumer;
@@ -109,13 +108,13 @@
String mainClass = addMainToClasspath("properties/MutablePropertyKt",
"mutableProperty_noUseOfProperties");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- checkClassIsRemoved(codeInspector, MUTABLE_PROPERTY_CLASS.getClassName());
- });
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ checkClassIsRemoved(inspector, MUTABLE_PROPERTY_CLASS.getClassName());
+ });
}
@Test
@@ -123,28 +122,28 @@
String mainClass = addMainToClasspath("properties/MutablePropertyKt",
"mutableProperty_usePrivateProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject classSubject =
- checkClassIsKept(codeInspector, MUTABLE_PROPERTY_CLASS.getClassName());
- String propertyName = "privateProp";
- FieldSubject fieldSubject =
- checkFieldIsKept(classSubject, JAVA_LANG_STRING, propertyName);
- if (!allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- }
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject classSubject =
+ checkClassIsKept(inspector, MUTABLE_PROPERTY_CLASS.getClassName());
+ String propertyName = "privateProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(classSubject, JAVA_LANG_STRING, propertyName);
+ if (!allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ }
- // Private property has no getter or setter.
- checkMethodIsAbsent(
- classSubject, MUTABLE_PROPERTY_CLASS.getGetterForProperty(propertyName));
- checkMethodIsAbsent(
- classSubject, MUTABLE_PROPERTY_CLASS.getSetterForProperty(propertyName));
- });
+ // Private property has no getter or setter.
+ checkMethodIsAbsent(
+ classSubject, MUTABLE_PROPERTY_CLASS.getGetterForProperty(propertyName));
+ checkMethodIsAbsent(
+ classSubject, MUTABLE_PROPERTY_CLASS.getSetterForProperty(propertyName));
+ });
}
@Test
@@ -152,27 +151,27 @@
String mainClass = addMainToClasspath("properties/MutablePropertyKt",
"mutableProperty_useProtectedProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject classSubject =
- checkClassIsKept(codeInspector, MUTABLE_PROPERTY_CLASS.getClassName());
- String propertyName = "protectedProp";
- FieldSubject fieldSubject =
- checkFieldIsKept(classSubject, JAVA_LANG_STRING, propertyName);
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject classSubject =
+ checkClassIsKept(inspector, MUTABLE_PROPERTY_CLASS.getClassName());
+ String propertyName = "protectedProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(classSubject, JAVA_LANG_STRING, propertyName);
- // Protected property has private field.
- MethodSignature getter = MUTABLE_PROPERTY_CLASS.getGetterForProperty(propertyName);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- checkMethodIsRemoved(classSubject, getter);
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- checkMethodIsKept(classSubject, getter);
- }
- });
+ // Protected property has private field.
+ MethodSignature getter = MUTABLE_PROPERTY_CLASS.getGetterForProperty(propertyName);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsRemoved(classSubject, getter);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsKept(classSubject, getter);
+ }
+ });
}
@Test
@@ -180,27 +179,27 @@
String mainClass = addMainToClasspath("properties/MutablePropertyKt",
"mutableProperty_useInternalProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject classSubject =
- checkClassIsKept(codeInspector, MUTABLE_PROPERTY_CLASS.getClassName());
- String propertyName = "internalProp";
- FieldSubject fieldSubject =
- checkFieldIsKept(classSubject, JAVA_LANG_STRING, propertyName);
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject classSubject =
+ checkClassIsKept(inspector, MUTABLE_PROPERTY_CLASS.getClassName());
+ String propertyName = "internalProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(classSubject, JAVA_LANG_STRING, propertyName);
- // Internal property has private field
- MethodSignature getter = MUTABLE_PROPERTY_CLASS.getGetterForProperty(propertyName);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- checkMethodIsRemoved(classSubject, getter);
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- checkMethodIsKept(classSubject, getter);
- }
- });
+ // Internal property has private field
+ MethodSignature getter = MUTABLE_PROPERTY_CLASS.getGetterForProperty(propertyName);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsRemoved(classSubject, getter);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsKept(classSubject, getter);
+ }
+ });
}
@Test
@@ -208,27 +207,27 @@
String mainClass = addMainToClasspath("properties/MutablePropertyKt",
"mutableProperty_usePublicProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject classSubject =
- checkClassIsKept(codeInspector, MUTABLE_PROPERTY_CLASS.getClassName());
- String propertyName = "publicProp";
- FieldSubject fieldSubject =
- checkFieldIsKept(classSubject, JAVA_LANG_STRING, propertyName);
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject classSubject =
+ checkClassIsKept(inspector, MUTABLE_PROPERTY_CLASS.getClassName());
+ String propertyName = "publicProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(classSubject, JAVA_LANG_STRING, propertyName);
- // Public property has private field
- MethodSignature getter = MUTABLE_PROPERTY_CLASS.getGetterForProperty(propertyName);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- checkMethodIsRemoved(classSubject, getter);
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- checkMethodIsKept(classSubject, getter);
- }
- });
+ // Public property has private field
+ MethodSignature getter = MUTABLE_PROPERTY_CLASS.getGetterForProperty(propertyName);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsRemoved(classSubject, getter);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsKept(classSubject, getter);
+ }
+ });
}
@Test
@@ -236,28 +235,28 @@
String mainClass = addMainToClasspath("properties/MutablePropertyKt",
"mutableProperty_usePrimitiveProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject classSubject =
- checkClassIsKept(codeInspector, MUTABLE_PROPERTY_CLASS.getClassName());
- String propertyName = "primitiveProp";
- FieldSubject fieldSubject = checkFieldIsKept(classSubject, "int", propertyName);
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject classSubject =
+ checkClassIsKept(inspector, MUTABLE_PROPERTY_CLASS.getClassName());
+ String propertyName = "primitiveProp";
+ FieldSubject fieldSubject = checkFieldIsKept(classSubject, "int", propertyName);
- MethodSignature getter = MUTABLE_PROPERTY_CLASS.getGetterForProperty(propertyName);
- MethodSignature setter = MUTABLE_PROPERTY_CLASS.getSetterForProperty(propertyName);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- checkMethodIsRemoved(classSubject, getter);
- checkMethodIsRemoved(classSubject, setter);
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- checkMethodIsKept(classSubject, getter);
- checkMethodIsKept(classSubject, setter);
- }
- });
+ MethodSignature getter = MUTABLE_PROPERTY_CLASS.getGetterForProperty(propertyName);
+ MethodSignature setter = MUTABLE_PROPERTY_CLASS.getSetterForProperty(propertyName);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsRemoved(classSubject, getter);
+ checkMethodIsRemoved(classSubject, setter);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsKept(classSubject, getter);
+ checkMethodIsKept(classSubject, setter);
+ }
+ });
}
@Test
@@ -265,13 +264,13 @@
String mainClass =
addMainToClasspath("properties/LateInitPropertyKt", "lateInitProperty_noUseOfProperties");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- checkClassIsRemoved(codeInspector, LATE_INIT_PROPERTY_CLASS.getClassName());
- });
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ checkClassIsRemoved(inspector, LATE_INIT_PROPERTY_CLASS.getClassName());
+ });
}
@Test
@@ -279,26 +278,26 @@
String mainClass = addMainToClasspath(
"properties/LateInitPropertyKt", "lateInitProperty_usePrivateLateInitProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject classSubject =
- checkClassIsKept(codeInspector, LATE_INIT_PROPERTY_CLASS.getClassName());
- String propertyName = "privateLateInitProp";
- FieldSubject fieldSubject = classSubject.field(JAVA_LANG_STRING, propertyName);
- assertTrue("Field is absent", fieldSubject.isPresent());
- if (!allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- }
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject classSubject =
+ checkClassIsKept(inspector, LATE_INIT_PROPERTY_CLASS.getClassName());
+ String propertyName = "privateLateInitProp";
+ FieldSubject fieldSubject = classSubject.field(JAVA_LANG_STRING, propertyName);
+ assertTrue("Field is absent", fieldSubject.isPresent());
+ if (!allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ }
- // Private late init property have no getter or setter.
- checkMethodIsAbsent(
- classSubject, LATE_INIT_PROPERTY_CLASS.getGetterForProperty(propertyName));
- checkMethodIsAbsent(
- classSubject, LATE_INIT_PROPERTY_CLASS.getSetterForProperty(propertyName));
- });
+ // Private late init property have no getter or setter.
+ checkMethodIsAbsent(
+ classSubject, LATE_INIT_PROPERTY_CLASS.getGetterForProperty(propertyName));
+ checkMethodIsAbsent(
+ classSubject, LATE_INIT_PROPERTY_CLASS.getSetterForProperty(propertyName));
+ });
}
@Test
@@ -306,24 +305,24 @@
String mainClass = addMainToClasspath("properties/LateInitPropertyKt",
"lateInitProperty_useProtectedLateInitProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject classSubject =
- checkClassIsKept(codeInspector, LATE_INIT_PROPERTY_CLASS.getClassName());
- String propertyName = "protectedLateInitProp";
- FieldSubject fieldSubject = classSubject.field(JAVA_LANG_STRING, propertyName);
- assertTrue("Field is absent", fieldSubject.isPresent());
- if (!allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isProtected());
- }
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject classSubject =
+ checkClassIsKept(inspector, LATE_INIT_PROPERTY_CLASS.getClassName());
+ String propertyName = "protectedLateInitProp";
+ FieldSubject fieldSubject = classSubject.field(JAVA_LANG_STRING, propertyName);
+ assertTrue("Field is absent", fieldSubject.isPresent());
+ if (!allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isProtected());
+ }
- // Protected late init property have protected getter
- checkMethodIsRemoved(
- classSubject, LATE_INIT_PROPERTY_CLASS.getGetterForProperty(propertyName));
- });
+ // Protected late init property have protected getter
+ checkMethodIsRemoved(
+ classSubject, LATE_INIT_PROPERTY_CLASS.getGetterForProperty(propertyName));
+ });
}
@Test
@@ -331,22 +330,22 @@
String mainClass = addMainToClasspath(
"properties/LateInitPropertyKt", "lateInitProperty_useInternalLateInitProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject classSubject =
- checkClassIsKept(codeInspector, LATE_INIT_PROPERTY_CLASS.getClassName());
- String propertyName = "internalLateInitProp";
- FieldSubject fieldSubject = classSubject.field(JAVA_LANG_STRING, propertyName);
- assertTrue("Field is absent", fieldSubject.isPresent());
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject classSubject =
+ checkClassIsKept(inspector, LATE_INIT_PROPERTY_CLASS.getClassName());
+ String propertyName = "internalLateInitProp";
+ FieldSubject fieldSubject = classSubject.field(JAVA_LANG_STRING, propertyName);
+ assertTrue("Field is absent", fieldSubject.isPresent());
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
- // Internal late init property have protected getter
- checkMethodIsRemoved(
- classSubject, LATE_INIT_PROPERTY_CLASS.getGetterForProperty(propertyName));
- });
+ // Internal late init property have protected getter
+ checkMethodIsRemoved(
+ classSubject, LATE_INIT_PROPERTY_CLASS.getGetterForProperty(propertyName));
+ });
}
@Test
@@ -354,22 +353,22 @@
String mainClass = addMainToClasspath(
"properties/LateInitPropertyKt", "lateInitProperty_usePublicLateInitProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject classSubject =
- checkClassIsKept(codeInspector, LATE_INIT_PROPERTY_CLASS.getClassName());
- String propertyName = "publicLateInitProp";
- FieldSubject fieldSubject = classSubject.field(JAVA_LANG_STRING, propertyName);
- assertTrue("Field is absent", fieldSubject.isPresent());
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject classSubject =
+ checkClassIsKept(inspector, LATE_INIT_PROPERTY_CLASS.getClassName());
+ String propertyName = "publicLateInitProp";
+ FieldSubject fieldSubject = classSubject.field(JAVA_LANG_STRING, propertyName);
+ assertTrue("Field is absent", fieldSubject.isPresent());
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
- // Internal late init property have protected getter
- checkMethodIsRemoved(
- classSubject, LATE_INIT_PROPERTY_CLASS.getGetterForProperty(propertyName));
- });
+ // Internal late init property have protected getter
+ checkMethodIsRemoved(
+ classSubject, LATE_INIT_PROPERTY_CLASS.getGetterForProperty(propertyName));
+ });
}
@Test
@@ -377,13 +376,13 @@
String mainClass = addMainToClasspath(
"properties/UserDefinedPropertyKt", "userDefinedProperty_noUseOfProperties");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- checkClassIsRemoved(codeInspector, USER_DEFINED_PROPERTY_CLASS.getClassName());
- });
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ checkClassIsRemoved(inspector, USER_DEFINED_PROPERTY_CLASS.getClassName());
+ });
}
@Test
@@ -391,29 +390,30 @@
String mainClass = addMainToClasspath(
"properties/UserDefinedPropertyKt", "userDefinedProperty_useProperties");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject classSubject =
- checkClassIsKept(codeInspector, USER_DEFINED_PROPERTY_CLASS.getClassName());
- String propertyName = "durationInSeconds";
- // The 'wrapper' property is not assigned to a backing field, it only relies on the
- // wrapped property.
- checkFieldIsAbsent(classSubject, "int", "durationInSeconds");
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject classSubject =
+ checkClassIsKept(inspector, USER_DEFINED_PROPERTY_CLASS.getClassName());
+ String propertyName = "durationInSeconds";
+ // The 'wrapper' property is not assigned to a backing field, it only relies on the
+ // wrapped property.
+ checkFieldIsAbsent(classSubject, "int", "durationInSeconds");
- FieldSubject fieldSubject =
- checkFieldIsKept(classSubject, "int", "durationInMilliSeconds");
- MethodSignature getter = USER_DEFINED_PROPERTY_CLASS.getGetterForProperty(propertyName);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- checkMethodIsRemoved(classSubject, getter);
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- checkMethodIsKept(classSubject, getter);
- }
- });
+ FieldSubject fieldSubject =
+ checkFieldIsKept(classSubject, "int", "durationInMilliSeconds");
+ MethodSignature getter =
+ USER_DEFINED_PROPERTY_CLASS.getGetterForProperty(propertyName);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsRemoved(classSubject, getter);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsKept(classSubject, getter);
+ }
+ });
}
@Test
@@ -421,32 +421,32 @@
String mainClass = addMainToClasspath(
"properties.CompanionPropertiesKt", "companionProperties_usePrimitiveProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject outerClass =
- checkClassIsKept(codeInspector, "properties.CompanionProperties");
- ClassSubject companionClass =
- checkClassIsKept(codeInspector, COMPANION_PROPERTY_CLASS.getClassName());
- String propertyName = "primitiveProp";
- FieldSubject fieldSubject = checkFieldIsKept(outerClass, "int", propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- }
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject outerClass =
+ checkClassIsKept(inspector, "properties.CompanionProperties");
+ ClassSubject companionClass =
+ checkClassIsKept(inspector, COMPANION_PROPERTY_CLASS.getClassName());
+ String propertyName = "primitiveProp";
+ FieldSubject fieldSubject = checkFieldIsKept(outerClass, "int", propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ }
- MemberNaming.MethodSignature getter =
- COMPANION_PROPERTY_CLASS.getGetterForProperty(propertyName);
- checkMethodIsRemoved(companionClass, getter);
+ MemberNaming.MethodSignature getter =
+ COMPANION_PROPERTY_CLASS.getGetterForProperty(propertyName);
+ checkMethodIsRemoved(companionClass, getter);
- MemberNaming.MethodSignature setter =
- COMPANION_PROPERTY_CLASS.getSetterForProperty(propertyName);
- checkMethodIsRemoved(companionClass, setter);
- });
+ MemberNaming.MethodSignature setter =
+ COMPANION_PROPERTY_CLASS.getSetterForProperty(propertyName);
+ checkMethodIsRemoved(companionClass, setter);
+ });
}
@Test
@@ -454,37 +454,38 @@
String mainClass = addMainToClasspath(
"properties.CompanionPropertiesKt", "companionProperties_usePrivateProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject outerClass =
- checkClassIsKept(codeInspector, "properties.CompanionProperties");
- ClassSubject companionClass =
- checkClassIsKept(codeInspector, COMPANION_PROPERTY_CLASS.getClassName());
- String propertyName = "privateProp";
- FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject outerClass =
+ checkClassIsKept(inspector, "properties.CompanionProperties");
+ ClassSubject companionClass =
+ checkClassIsKept(inspector, COMPANION_PROPERTY_CLASS.getClassName());
+ String propertyName = "privateProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getter =
- COMPANION_PROPERTY_CLASS.getGetterForProperty(propertyName);
- MemberNaming.MethodSignature setter =
- COMPANION_PROPERTY_CLASS.getSetterForProperty(propertyName);
+ MemberNaming.MethodSignature getter =
+ COMPANION_PROPERTY_CLASS.getGetterForProperty(propertyName);
+ MemberNaming.MethodSignature setter =
+ COMPANION_PROPERTY_CLASS.getSetterForProperty(propertyName);
- // Because the getter/setter are private, they can only be called from another method in
- // the class. If this is an instance method, they will be called on 'this' which is known
- // to be non-null, thus the getter/setter can be inlined if their code is small enough.
- // Because the backing field is private, they will call into an accessor (static) method.
- // If access relaxation is enabled, this accessor can be removed.
- checkMethodIsAbsent(companionClass, getter);
- checkMethodIsAbsent(companionClass, setter);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- }
- });
+ // Because the getter/setter are private, they can only be called from another method
+ // in the class. If this is an instance method, they will be called on 'this' which is
+ // known to be non-null, thus the getter/setter can be inlined if their code is small
+ // enough. Because the backing field is private, they will call into an accessor
+ // (static) method. If access relaxation is enabled, this accessor can be removed.
+ checkMethodIsAbsent(companionClass, getter);
+ checkMethodIsAbsent(companionClass, setter);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ }
+ });
}
@Test
@@ -492,31 +493,32 @@
String mainClass = addMainToClasspath(
"properties.CompanionPropertiesKt", "companionProperties_useInternalProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject outerClass =
- checkClassIsKept(codeInspector, "properties.CompanionProperties");
- ClassSubject companionClass =
- checkClassIsKept(codeInspector, COMPANION_PROPERTY_CLASS.getClassName());
- String propertyName = "internalProp";
- FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- }
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject outerClass =
+ checkClassIsKept(inspector, "properties.CompanionProperties");
+ ClassSubject companionClass =
+ checkClassIsKept(inspector, COMPANION_PROPERTY_CLASS.getClassName());
+ String propertyName = "internalProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ }
- MemberNaming.MethodSignature getter =
- COMPANION_PROPERTY_CLASS.getGetterForProperty(propertyName);
- checkMethodIsRemoved(companionClass, getter);
- MemberNaming.MethodSignature setter =
- COMPANION_PROPERTY_CLASS.getSetterForProperty(propertyName);
- checkMethodIsRemoved(companionClass, setter);
- });
+ MemberNaming.MethodSignature getter =
+ COMPANION_PROPERTY_CLASS.getGetterForProperty(propertyName);
+ checkMethodIsRemoved(companionClass, getter);
+ MemberNaming.MethodSignature setter =
+ COMPANION_PROPERTY_CLASS.getSetterForProperty(propertyName);
+ checkMethodIsRemoved(companionClass, setter);
+ });
}
@Test
@@ -524,32 +526,33 @@
String mainClass = addMainToClasspath(
"properties.CompanionPropertiesKt", "companionProperties_usePublicProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject outerClass =
- checkClassIsKept(codeInspector, "properties.CompanionProperties");
- ClassSubject companionClass =
- checkClassIsKept(codeInspector, COMPANION_PROPERTY_CLASS.getClassName());
- String propertyName = "publicProp";
- FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- }
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject outerClass =
+ checkClassIsKept(inspector, "properties.CompanionProperties");
+ ClassSubject companionClass =
+ checkClassIsKept(inspector, COMPANION_PROPERTY_CLASS.getClassName());
+ String propertyName = "publicProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ }
- MemberNaming.MethodSignature getter =
- COMPANION_PROPERTY_CLASS.getGetterForProperty(propertyName);
- checkMethodIsRemoved(companionClass, getter);
+ MemberNaming.MethodSignature getter =
+ COMPANION_PROPERTY_CLASS.getGetterForProperty(propertyName);
+ checkMethodIsRemoved(companionClass, getter);
- MemberNaming.MethodSignature setter =
- COMPANION_PROPERTY_CLASS.getSetterForProperty(propertyName);
- checkMethodIsRemoved(companionClass, setter);
- });
+ MemberNaming.MethodSignature setter =
+ COMPANION_PROPERTY_CLASS.getSetterForProperty(propertyName);
+ checkMethodIsRemoved(companionClass, setter);
+ });
}
@Test
@@ -558,34 +561,35 @@
String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
"companionLateInitProperties_usePrivateLateInitProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject outerClass =
- checkClassIsKept(codeInspector, testedClass.getOuterClassName());
- ClassSubject companionClass = checkClassIsKept(codeInspector, testedClass.getClassName());
- String propertyName = "privateLateInitProp";
- FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject outerClass =
+ checkClassIsKept(inspector, testedClass.getOuterClassName());
+ ClassSubject companionClass = checkClassIsKept(inspector, testedClass.getClassName());
+ String propertyName = "privateLateInitProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
- MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
+ MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
- // Because the getter/setter are private, they can only be called from another method in
- // the class. If this is an instance method, they will be called on 'this' which is known
- // to be non-null, thus the getter/setter can be inlined if their code is small enough.
- // Because the backing field is private, they will call into an accessor (static) method.
- // If access relaxation is enabled, this accessor can be removed.
- checkMethodIsAbsent(companionClass, getter);
- checkMethodIsAbsent(companionClass, setter);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- }
- });
+ // Because the getter/setter are private, they can only be called from another method
+ // in the class. If this is an instance method, they will be called on 'this' which is
+ // known to be non-null, thus the getter/setter can be inlined if their code is small
+ // enough. Because the backing field is private, they will call into an accessor
+ // (static) method. If access relaxation is enabled, this accessor can be removed.
+ checkMethodIsAbsent(companionClass, getter);
+ checkMethodIsAbsent(companionClass, setter);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ }
+ });
}
@Test
@@ -594,25 +598,26 @@
String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
"companionLateInitProperties_useInternalLateInitProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject outerClass =
- checkClassIsKept(codeInspector, testedClass.getOuterClassName());
- ClassSubject companionClass = checkClassIsKept(codeInspector, testedClass.getClassName());
- String propertyName = "internalLateInitProp";
- FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject outerClass =
+ checkClassIsKept(inspector, testedClass.getOuterClassName());
+ ClassSubject companionClass = checkClassIsKept(inspector, testedClass.getClassName());
+ String propertyName = "internalLateInitProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
- MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
- checkMethodIsRemoved(companionClass, getter);
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
+ checkMethodIsRemoved(companionClass, getter);
- MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
- checkMethodIsRemoved(companionClass, setter);
- });
+ MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
+ checkMethodIsRemoved(companionClass, setter);
+ });
}
@Test
@@ -621,25 +626,26 @@
String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
"companionLateInitProperties_usePublicLateInitProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject outerClass =
- checkClassIsKept(codeInspector, testedClass.getOuterClassName());
- ClassSubject companionClass = checkClassIsKept(codeInspector, testedClass.getClassName());
- String propertyName = "publicLateInitProp";
- FieldSubject fieldSubject = checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject outerClass =
+ checkClassIsKept(inspector, testedClass.getOuterClassName());
+ ClassSubject companionClass = checkClassIsKept(inspector, testedClass.getClassName());
+ String propertyName = "publicLateInitProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(outerClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
- MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
- checkMethodIsRemoved(companionClass, getter);
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
+ checkMethodIsRemoved(companionClass, getter);
- MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
- checkMethodIsRemoved(companionClass, setter);
- });
+ MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
+ checkMethodIsRemoved(companionClass, setter);
+ });
}
@Test
@@ -648,36 +654,36 @@
String mainClass = addMainToClasspath(
"properties.ObjectPropertiesKt", "objectProperties_usePrimitiveProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject objectClass = checkClassIsKept(codeInspector, testedClass.getClassName());
- String propertyName = "primitiveProp";
- FieldSubject fieldSubject = checkFieldIsKept(objectClass, "int", propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
+ String propertyName = "primitiveProp";
+ FieldSubject fieldSubject = checkFieldIsKept(objectClass, "int", propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
- MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
+ MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
- if (allowAccessModification) {
- // Getter and setter is inlined because the constructor of ObjectProperties is
- // considered trivial, which implies that the member value propagation marks the
- // INSTANCE field as being non-null.
- checkMethodIsRemoved(objectClass, getter);
- checkMethodIsRemoved(objectClass, setter);
- } else {
- checkMethodIsKept(objectClass, getter);
- checkMethodIsKept(objectClass, setter);
- }
+ if (allowAccessModification) {
+ // Getter and setter is inlined because the constructor of ObjectProperties is
+ // considered trivial, which implies that the member value propagation marks the
+ // INSTANCE field as being non-null.
+ checkMethodIsRemoved(objectClass, getter);
+ checkMethodIsRemoved(objectClass, setter);
+ } else {
+ checkMethodIsKept(objectClass, getter);
+ checkMethodIsKept(objectClass, setter);
+ }
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- }
- });
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ }
+ });
}
@Test
@@ -686,29 +692,30 @@
String mainClass = addMainToClasspath(
"properties.ObjectPropertiesKt", "objectProperties_usePrivateProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject objectClass = checkClassIsKept(codeInspector, testedClass.getClassName());
- String propertyName = "privateProp";
- FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
+ String propertyName = "privateProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
- MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
+ MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
- // A private property has no getter/setter.
- checkMethodIsAbsent(objectClass, getter);
- checkMethodIsAbsent(objectClass, setter);
+ // A private property has no getter/setter.
+ checkMethodIsAbsent(objectClass, getter);
+ checkMethodIsAbsent(objectClass, setter);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- }
- });
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ }
+ });
}
@Test
@@ -717,36 +724,37 @@
String mainClass = addMainToClasspath(
"properties.ObjectPropertiesKt", "objectProperties_useInternalProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject objectClass = checkClassIsKept(codeInspector, testedClass.getClassName());
- String propertyName = "internalProp";
- FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
+ String propertyName = "internalProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
- MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
+ MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
- if (allowAccessModification) {
- // Getter and setter is inlined because the constructor of ObjectProperties is
- // considered trivial, which implies that the member value propagation marks the
- // INSTANCE field as being non-null.
- checkMethodIsRemoved(objectClass, getter);
- checkMethodIsRemoved(objectClass, setter);
- } else {
- checkMethodIsKept(objectClass, getter);
- checkMethodIsKept(objectClass, setter);
- }
+ if (allowAccessModification) {
+ // Getter and setter is inlined because the constructor of ObjectProperties is
+ // considered trivial, which implies that the member value propagation marks the
+ // INSTANCE field as being non-null.
+ checkMethodIsRemoved(objectClass, getter);
+ checkMethodIsRemoved(objectClass, setter);
+ } else {
+ checkMethodIsKept(objectClass, getter);
+ checkMethodIsKept(objectClass, setter);
+ }
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- }
- });
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ }
+ });
}
@Test
@@ -755,36 +763,37 @@
String mainClass = addMainToClasspath(
"properties.ObjectPropertiesKt", "objectProperties_usePublicProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject objectClass = checkClassIsKept(codeInspector, testedClass.getClassName());
- String propertyName = "publicProp";
- FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
+ String propertyName = "publicProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
- MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
+ MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
- if (allowAccessModification) {
- // Getter and setter is inlined because the constructor of ObjectProperties is
- // considered trivial, which implies that the member value propagation marks the
- // INSTANCE field as being non-null.
- checkMethodIsRemoved(objectClass, getter);
- checkMethodIsRemoved(objectClass, setter);
- } else {
- checkMethodIsKept(objectClass, getter);
- checkMethodIsKept(objectClass, setter);
- }
+ if (allowAccessModification) {
+ // Getter and setter is inlined because the constructor of ObjectProperties is
+ // considered trivial, which implies that the member value propagation marks the
+ // INSTANCE field as being non-null.
+ checkMethodIsRemoved(objectClass, getter);
+ checkMethodIsRemoved(objectClass, setter);
+ } else {
+ checkMethodIsKept(objectClass, getter);
+ checkMethodIsKept(objectClass, setter);
+ }
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- }
- });
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ }
+ });
}
@Test
@@ -793,29 +802,30 @@
String mainClass = addMainToClasspath(
"properties.ObjectPropertiesKt", "objectProperties_useLateInitPrivateProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject objectClass = checkClassIsKept(codeInspector, testedClass.getClassName());
- String propertyName = "privateLateInitProp";
- FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
+ String propertyName = "privateLateInitProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
- MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
+ MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
- // A private property has no getter/setter.
- checkMethodIsAbsent(objectClass, getter);
- checkMethodIsAbsent(objectClass, setter);
+ // A private property has no getter/setter.
+ checkMethodIsAbsent(objectClass, getter);
+ checkMethodIsAbsent(objectClass, setter);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- }
- });
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ }
+ });
}
@Test
@@ -824,23 +834,24 @@
String mainClass = addMainToClasspath(
"properties.ObjectPropertiesKt", "objectProperties_useLateInitInternalProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject objectClass = checkClassIsKept(codeInspector, testedClass.getClassName());
- String propertyName = "internalLateInitProp";
- FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
+ String propertyName = "internalLateInitProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
- MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
+ MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
- checkMethodIsRemoved(objectClass, getter);
- checkMethodIsRemoved(objectClass, setter);
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- });
+ checkMethodIsRemoved(objectClass, getter);
+ checkMethodIsRemoved(objectClass, setter);
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ });
}
@Test
@@ -849,23 +860,24 @@
String mainClass = addMainToClasspath(
"properties.ObjectPropertiesKt", "objectProperties_useLateInitPublicProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject objectClass = checkClassIsKept(codeInspector, testedClass.getClassName());
- String propertyName = "publicLateInitProp";
- FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
+ String propertyName = "publicLateInitProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
- MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
+ MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
- checkMethodIsRemoved(objectClass, getter);
- checkMethodIsRemoved(objectClass, setter);
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- });
+ checkMethodIsRemoved(objectClass, getter);
+ checkMethodIsRemoved(objectClass, setter);
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ });
}
@Test
@@ -874,29 +886,29 @@
String mainClass = addMainToClasspath(
"properties.FilePropertiesKt", "fileProperties_usePrimitiveProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject objectClass = checkClassIsKept(codeInspector, testedClass.getClassName());
- String propertyName = "primitiveProp";
- FieldSubject fieldSubject = checkFieldIsKept(objectClass, "int", propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
+ String propertyName = "primitiveProp";
+ FieldSubject fieldSubject = checkFieldIsKept(objectClass, "int", propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
- MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
+ MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- checkMethodIsRemoved(objectClass, getter);
- checkMethodIsRemoved(objectClass, setter);
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- checkMethodIsKept(objectClass, getter);
- checkMethodIsKept(objectClass, setter);
- }
- });
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsRemoved(objectClass, getter);
+ checkMethodIsRemoved(objectClass, setter);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsKept(objectClass, getter);
+ checkMethodIsKept(objectClass, setter);
+ }
+ });
}
@Test
@@ -905,28 +917,29 @@
String mainClass = addMainToClasspath(
"properties.FilePropertiesKt", "fileProperties_usePrivateProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject objectClass = checkClassIsKept(codeInspector, testedClass.getClassName());
- String propertyName = "privateProp";
- FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
+ String propertyName = "privateProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
- MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
+ MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
- // A private property has no getter/setter.
- checkMethodIsAbsent(objectClass, getter);
- checkMethodIsAbsent(objectClass, setter);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- }
- });
+ // A private property has no getter/setter.
+ checkMethodIsAbsent(objectClass, getter);
+ checkMethodIsAbsent(objectClass, setter);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ }
+ });
}
@Test
@@ -935,28 +948,31 @@
String mainClass = addMainToClasspath(
"properties.FilePropertiesKt", "fileProperties_useInternalProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject objectClass = checkClassIsKept(codeInspector, testedClass.getClassName());
- String propertyName = "internalProp";
- FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
+ String propertyName = "internalProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
- // We expect getter to be inlined when access (of the backing field) is relaxed to public.
- // Note: the setter is considered as a regular method (because of KotlinC adding extra
- // null checks), thus we cannot say if the setter would be inlined or not by R8.
- MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- checkMethodIsRemoved(objectClass, getter);
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- checkMethodIsKept(objectClass, getter);
- }
- });
+ // We expect getter to be inlined when access (of the backing field) is relaxed to
+ // public.
+ //
+ // Note: the setter is considered as a regular method (because of KotlinC adding extra
+ // null checks), thus we cannot say if the setter would be inlined or not by R8.
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsRemoved(objectClass, getter);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsKept(objectClass, getter);
+ }
+ });
}
@Test
@@ -965,29 +981,30 @@
String mainClass = addMainToClasspath(
"properties.FilePropertiesKt", "fileProperties_usePublicProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject objectClass = checkClassIsKept(codeInspector, testedClass.getClassName());
- String propertyName = "publicProp";
- FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
+ String propertyName = "publicProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
- // We expect getter to be inlined when access (of the backing field) is relaxed to public.
- // On the other hand, the setter is considered as a regular method (because of null
- // checks), thus we cannot say if it can be inlined or not.
- MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
+ // We expect getter to be inlined when access (of the backing field) is relaxed to
+ // public. On the other hand, the setter is considered as a regular method (because of
+ // null checks), thus we cannot say if it can be inlined or not.
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- checkMethodIsRemoved(objectClass, getter);
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- checkMethodIsKept(objectClass, getter);
- }
- });
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsRemoved(objectClass, getter);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsKept(objectClass, getter);
+ }
+ });
}
@Test
@@ -996,28 +1013,29 @@
String mainClass = addMainToClasspath(
"properties.FilePropertiesKt", "fileProperties_useLateInitPrivateProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject fileClass = checkClassIsKept(codeInspector, testedClass.getClassName());
- String propertyName = "privateLateInitProp";
- FieldSubject fieldSubject = checkFieldIsKept(fileClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject fileClass = checkClassIsKept(inspector, testedClass.getClassName());
+ String propertyName = "privateLateInitProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(fileClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
- MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
+ MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
- // A private property has no getter/setter.
- checkMethodIsAbsent(fileClass, getter);
- checkMethodIsAbsent(fileClass, setter);
- if (allowAccessModification) {
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- } else {
- assertTrue(fieldSubject.getField().accessFlags.isPrivate());
- }
- });
+ // A private property has no getter/setter.
+ checkMethodIsAbsent(fileClass, getter);
+ checkMethodIsAbsent(fileClass, setter);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ }
+ });
}
@Test
@@ -1026,25 +1044,26 @@
String mainClass = addMainToClasspath(
"properties.FilePropertiesKt", "fileProperties_useLateInitInternalProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject objectClass = checkClassIsKept(codeInspector, testedClass.getClassName());
- String propertyName = "internalLateInitProp";
- FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
+ String propertyName = "internalLateInitProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
- MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
- MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
+ MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
- // Field is public and getter/setter is only called from one place so we expect to always
- // inline it.
- checkMethodIsRemoved(objectClass, getter);
- checkMethodIsRemoved(objectClass, setter);
- });
+ // Field is public and getter/setter is only called from one place so we expect to
+ // always inline it.
+ checkMethodIsRemoved(objectClass, getter);
+ checkMethodIsRemoved(objectClass, setter);
+ });
}
@Test
@@ -1053,23 +1072,24 @@
String mainClass = addMainToClasspath(
"properties.FilePropertiesKt", "fileProperties_useLateInitPublicProp");
runTest(
- PACKAGE_NAME,
- mainClass,
- disableAggressiveClassOptimizations,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject objectClass = checkClassIsKept(codeInspector, testedClass.getClassName());
- String propertyName = "publicLateInitProp";
- FieldSubject fieldSubject = checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
- assertTrue(fieldSubject.getField().accessFlags.isStatic());
+ PACKAGE_NAME,
+ mainClass,
+ testBuilder -> testBuilder.addOptionsModification(disableAggressiveClassOptimizations))
+ .inspect(
+ inspector -> {
+ ClassSubject objectClass = checkClassIsKept(inspector, testedClass.getClassName());
+ String propertyName = "publicLateInitProp";
+ FieldSubject fieldSubject =
+ checkFieldIsKept(objectClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
- MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
+ MemberNaming.MethodSignature setter = testedClass.getSetterForProperty(propertyName);
- checkMethodIsRemoved(objectClass, getter);
- checkMethodIsRemoved(objectClass, setter);
- assertTrue(fieldSubject.getField().accessFlags.isPublic());
- });
+ checkMethodIsRemoved(objectClass, getter);
+ checkMethodIsRemoved(objectClass, setter);
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ });
}
}
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 a144a0e..d56d0f0 100644
--- a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
@@ -40,24 +39,21 @@
ImmutableList.of("java.util.Collection", STRING, STRING, "java.lang.Integer"));
final String mainClassName = ex1.getClassName();
- final String extraRules =
- keepMainMethod(mainClassName) + neverInlineMethod(mainClassName, testMethodSignature);
- runTest(
- FOLDER,
- mainClassName,
- extraRules,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject clazz = checkClassIsKept(codeInspector, ex1.getClassName());
+ final String extraRules = neverInlineMethod(mainClassName, testMethodSignature);
+ runTest(FOLDER, mainClassName, testBuilder -> testBuilder.addKeepRules(extraRules))
+ .inspect(
+ inspector -> {
+ ClassSubject clazz = checkClassIsKept(inspector, ex1.getClassName());
- MethodSubject testMethod = checkMethodIsKept(clazz, testMethodSignature);
- long ifzCount =
- testMethod.streamInstructions().filter(i -> i.isIfEqz() || i.isIfNez()).count();
- long paramNullCheckCount = countCall(testMethod, "Intrinsics", "checkParameterIsNotNull");
- // One after Iterator#hasNext, and another in the filter predicate: sinceYear != null.
- assertEquals(2, ifzCount);
- assertEquals(0, paramNullCheckCount);
- });
+ MethodSubject testMethod = checkMethodIsKept(clazz, testMethodSignature);
+ long ifzCount =
+ testMethod.streamInstructions().filter(i -> i.isIfEqz() || i.isIfNez()).count();
+ long paramNullCheckCount =
+ countCall(testMethod, "Intrinsics", "checkParameterIsNotNull");
+ // One after Iterator#hasNext, and another in the filter predicate: sinceYear != null.
+ assertEquals(2, ifzCount);
+ assertEquals(0, paramNullCheckCount);
+ });
}
@Test
@@ -67,22 +63,21 @@
new MethodSignature("aOrDefault", STRING, ImmutableList.of(STRING, STRING));
final String mainClassName = ex2.getClassName();
- final String extraRules =
- keepMainMethod(mainClassName) + neverInlineMethod(mainClassName, testMethodSignature);
- runTest(FOLDER, mainClassName, extraRules,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject clazz = checkClassIsKept(codeInspector, ex2.getClassName());
+ final String extraRules = neverInlineMethod(mainClassName, testMethodSignature);
+ runTest(FOLDER, mainClassName, testBuilder -> testBuilder.addKeepRules(extraRules))
+ .inspect(
+ inspector -> {
+ ClassSubject clazz = checkClassIsKept(inspector, ex2.getClassName());
- MethodSubject testMethod = checkMethodIsKept(clazz, testMethodSignature);
- long ifzCount =
- testMethod.streamInstructions().filter(i -> i.isIfEqz() || i.isIfNez()).count();
- long paramNullCheckCount =
- countCall(testMethod, "Intrinsics", "checkParameterIsNotNull");
- // ?: in aOrDefault
- assertEquals(1, ifzCount);
- assertEquals(allowAccessModification ? 0 : 1, paramNullCheckCount);
- });
+ MethodSubject testMethod = checkMethodIsKept(clazz, testMethodSignature);
+ long ifzCount =
+ testMethod.streamInstructions().filter(i -> i.isIfEqz() || i.isIfNez()).count();
+ long paramNullCheckCount =
+ countCall(testMethod, "Intrinsics", "checkParameterIsNotNull");
+ // ?: in aOrDefault
+ assertEquals(1, ifzCount);
+ assertEquals(allowAccessModification ? 0 : 1, paramNullCheckCount);
+ });
}
@Test
@@ -92,19 +87,18 @@
new MethodSignature("neverThrowNPE", "void", ImmutableList.of("non_null.Foo"));
final String mainClassName = ex3.getClassName();
- final String extraRules =
- keepMainMethod(mainClassName) + neverInlineMethod(mainClassName, testMethodSignature);
- runTest(FOLDER, mainClassName, extraRules,
- app -> {
- CodeInspector codeInspector = new CodeInspector(app);
- ClassSubject clazz = checkClassIsKept(codeInspector, ex3.getClassName());
+ final String extraRules = neverInlineMethod(mainClassName, testMethodSignature);
+ runTest(FOLDER, mainClassName, testBuilder -> testBuilder.addKeepRules(extraRules))
+ .inspect(
+ inspector -> {
+ ClassSubject clazz = checkClassIsKept(inspector, ex3.getClassName());
- MethodSubject testMethod = checkMethodIsKept(clazz, testMethodSignature);
- long ifzCount =
- testMethod.streamInstructions().filter(i -> i.isIfEqz() || i.isIfNez()).count();
- // !! operator inside explicit null check should be gone.
- // One explicit null-check as well as 4 bar? accesses.
- assertEquals(5, ifzCount);
- });
+ MethodSubject testMethod = checkMethodIsKept(clazz, testMethodSignature);
+ long ifzCount =
+ testMethod.streamInstructions().filter(i -> i.isIfEqz() || i.isIfNez()).count();
+ // !! operator inside explicit null check should be gone.
+ // One explicit null-check as well as 4 bar? accesses.
+ assertEquals(5, ifzCount);
+ });
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
index bf17ac0..f28f68b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.kotlin.lambda;
import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assume.assumeTrue;
@@ -55,11 +54,8 @@
.addLibraryFiles(ToolHelper.getKotlinStdlibJar())
.addProgramFiles(ktClasses)
.addKeepMainRule("**.B143165163Kt")
- .allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
.compile()
- // TODO(b/143165163): better not output info like this.
- .assertAllInfoMessagesMatch(containsString("unexpected static method"))
.addRunClasspathFiles(ToolHelper.getKotlinStdlibJar())
.run(parameters.getRuntime(), pkg + ".B143165163Kt")
.assertSuccessWithOutputLines("outer foo bar", "outer foo default");
@@ -81,11 +77,9 @@
.addProgramFiles(ToolHelper.getKotlinStdlibJar())
.addProgramFiles(ktClasses)
.addKeepMainRule("**.B143165163Kt")
- .allowDiagnosticMessages()
+ .allowDiagnosticWarningMessages()
.setMinApi(parameters.getApiLevel())
.compile()
- // TODO(b/143165163): better not output info like this.
- .assertAllInfoMessagesMatch(containsString("does not implement any interfaces"))
.assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.run(parameters.getRuntime(), pkg + ".B143165163Kt")
.assertSuccessWithOutputLines("outer foo bar", "outer foo default");
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
index 9f51143..4f3ea88 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
@@ -15,18 +15,14 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.optimize.lambda.CaptureSignature;
import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
-import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.Lists;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
-import java.util.concurrent.ExecutionException;
-import java.util.function.Consumer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -35,21 +31,16 @@
public class KotlinLambdaMergingTest extends AbstractR8KotlinTestBase {
private static final String KOTLIN_FUNCTION_IFACE = "Lkotlin/jvm/functions/Function";
private static final String KOTLIN_FUNCTION_IFACE_STR = "kotlin.jvm.functions.Function";
- private static final String KEEP_INNER_AND_ENCLOSING =
- "-keepattributes InnerClasses,EnclosingMethod\n";
- private static final String KEEP_SIGNATURE_INNER_ENCLOSING =
- "-keepattributes Signature,InnerClasses,EnclosingMethod\n";
- private Consumer<InternalOptions> getOptionsModifier() {
- return opts -> {
- opts.enableClassInlining = false;
- // The test checks that the generated lambdas inherit from Function, which is not true if
- // the unused interface removal is enabled.
- opts.enableUnusedInterfaceRemoval = enableUnusedInterfaceRemoval;
- // Ensure that enclosing method and inner class attributes are kept even on classes that are
- // not explicitly mentioned by a keep rule.
- opts.forceProguardCompatibility = true;
- };
+ private void configure(InternalOptions options) {
+ options.enableClassInlining = false;
+ // The test checks that the generated lambdas inherit from Function, which is not true if
+ // the unused interface removal is enabled.
+ options.enableUnusedInterfaceRemoval = enableUnusedInterfaceRemoval;
+ // Ensure that enclosing method and inner class attributes are kept even on classes that are
+ // not explicitly mentioned by a keep rule.
+ options.forceProguardCompatibility = true;
+ options.enableHorizontalClassMergingOfKotlinLambdas = false;
}
private final boolean enableUnusedInterfaceRemoval;
@@ -173,10 +164,6 @@
final List<DexClass> lambdas = new ArrayList<>();
final List<DexClass> groups = new ArrayList<>();
- Verifier(AndroidApp app) throws IOException, ExecutionException {
- this(new CodeInspector(app));
- }
-
Verifier(CodeInspector codeInspector) {
this.codeInspector = codeInspector;
initGroupsAndLambdas();
@@ -304,242 +291,264 @@
public void testTrivialKs() throws Exception {
final String mainClassName = "lambdas_kstyle_trivial.MainKt";
runTest(
- "lambdas_kstyle_trivial",
- mainClassName,
- "-keepunusedarguments class * extends kotlin.jvm.internal.Lambda { invoke(int, short); }",
- getOptionsModifier(),
- app -> {
- if (enableUnusedInterfaceRemoval) {
- // Only test that the code generates the same output as the input code does on the JVM.
- return;
- }
+ "lambdas_kstyle_trivial",
+ mainClassName,
+ testBuilder ->
+ testBuilder
+ .addKeepRules(
+ "-keepunusedarguments class * extends kotlin.jvm.internal.Lambda {"
+ + " invoke(int, short); }")
+ .addOptionsModification(this::configure))
+ .inspect(
+ inspector -> {
+ if (enableUnusedInterfaceRemoval) {
+ // Only test that the code generates the same output as the input code does on the
+ // JVM.
+ return;
+ }
- Verifier verifier = new Verifier(app);
- String pkg = "lambdas_kstyle_trivial";
+ Verifier verifier = new Verifier(inspector);
+ String pkg = "lambdas_kstyle_trivial";
- verifier.assertLambdaGroups(
- allowAccessModification
- ? new Group[] {
- kstyle("", 0, 4),
- kstyle("", 1, 9),
- kstyle("", 2, 2), // -\
- kstyle("", 2, 5), // - 3 groups different by main method
- kstyle("", 2, 4), // -/
- kstyle("", 3, 2),
- kstyle("", 22, 2)
- }
- : new Group[] {
- kstyle(pkg, 0, 2),
- kstyle(pkg, 1, 5),
- kstyle(pkg, 2, 5), // - 2 groups different by main method
- kstyle(pkg, 2, 4), // -/
- kstyle(pkg, 3, 2),
- kstyle(pkg, 22, 2),
- kstyle(pkg + "/inner", 0, 2),
- kstyle(pkg + "/inner", 1, 4)
- });
+ verifier.assertLambdaGroups(
+ allowAccessModification
+ ? new Group[] {
+ kstyle("", 0, 4),
+ kstyle("", 1, 9),
+ kstyle("", 2, 2), // -\
+ kstyle("", 2, 5), // - 3 groups different by main method
+ kstyle("", 2, 4), // -/
+ kstyle("", 3, 2),
+ kstyle("", 22, 2)
+ }
+ : new Group[] {
+ kstyle(pkg, 0, 2),
+ kstyle(pkg, 1, 5),
+ kstyle(pkg, 2, 5), // - 2 groups different by main method
+ kstyle(pkg, 2, 4), // -/
+ kstyle(pkg, 3, 2),
+ kstyle(pkg, 22, 2),
+ kstyle(pkg + "/inner", 0, 2),
+ kstyle(pkg + "/inner", 1, 4)
+ });
- verifier.assertLambdas(
- allowAccessModification
- ? new Lambda[] {}
- : new Lambda[] {
- new Lambda(pkg, "MainKt$testStateless$8", 2),
- new Lambda(pkg + "/inner", "InnerKt$testInnerStateless$7", 2)
- });
- });
+ verifier.assertLambdas(
+ allowAccessModification
+ ? new Lambda[] {}
+ : new Lambda[] {
+ new Lambda(pkg, "MainKt$testStateless$8", 2),
+ new Lambda(pkg + "/inner", "InnerKt$testInnerStateless$7", 2)
+ });
+ });
}
@Test
public void testCapturesKs() throws Exception {
final String mainClassName = "lambdas_kstyle_captures.MainKt";
runTest(
- "lambdas_kstyle_captures",
- mainClassName,
- getOptionsModifier(),
- app -> {
- if (enableUnusedInterfaceRemoval) {
- // Only test that the code generates the same output as the input code does on the JVM.
- return;
- }
+ "lambdas_kstyle_captures",
+ mainClassName,
+ testBuilder -> testBuilder.addOptionsModification(this::configure))
+ .inspect(
+ inspector -> {
+ if (enableUnusedInterfaceRemoval) {
+ // Only test that the code generates the same output as the input code does on the
+ // JVM.
+ return;
+ }
- Verifier verifier = new Verifier(app);
- String pkg = "lambdas_kstyle_captures";
- String grpPkg = allowAccessModification ? "" : pkg;
+ Verifier verifier = new Verifier(inspector);
+ String pkg = "lambdas_kstyle_captures";
+ String grpPkg = allowAccessModification ? "" : pkg;
- verifier.assertLambdaGroups(
- kstyle(grpPkg, "LLL", 0),
- kstyle(grpPkg, "ILL", 0),
- kstyle(grpPkg, "III", 0),
- kstyle(grpPkg, "BCDFIJLLLLSZ", 0),
- kstyle(grpPkg, "BCDFIJLLSZ", 0));
+ verifier.assertLambdaGroups(
+ kstyle(grpPkg, "LLL", 0),
+ kstyle(grpPkg, "ILL", 0),
+ kstyle(grpPkg, "III", 0),
+ kstyle(grpPkg, "BCDFIJLLLLSZ", 0),
+ kstyle(grpPkg, "BCDFIJLLSZ", 0));
- verifier.assertLambdas(
- new Lambda(pkg, "MainKt$test1$15", 0),
- new Lambda(pkg, "MainKt$test2$10", 0),
- new Lambda(pkg, "MainKt$test2$11", 0),
- new Lambda(pkg, "MainKt$test2$9", 0));
- });
+ verifier.assertLambdas(
+ new Lambda(pkg, "MainKt$test1$15", 0),
+ new Lambda(pkg, "MainKt$test2$10", 0),
+ new Lambda(pkg, "MainKt$test2$11", 0),
+ new Lambda(pkg, "MainKt$test2$9", 0));
+ });
}
@Test
public void testGenericsNoSignatureKs() throws Exception {
final String mainClassName = "lambdas_kstyle_generics.MainKt";
runTest(
- "lambdas_kstyle_generics",
- mainClassName,
- getOptionsModifier(),
- app -> {
- if (enableUnusedInterfaceRemoval) {
- // Only test that the code generates the same output as the input code does on the JVM.
- return;
- }
+ "lambdas_kstyle_generics",
+ mainClassName,
+ testBuilder -> testBuilder.addOptionsModification(this::configure))
+ .inspect(
+ inspector -> {
+ if (enableUnusedInterfaceRemoval) {
+ // Only test that the code generates the same output as the input code does on the
+ // JVM.
+ return;
+ }
- Verifier verifier = new Verifier(app);
- String pkg = "lambdas_kstyle_generics";
- String grpPkg = allowAccessModification ? "" : pkg;
+ Verifier verifier = new Verifier(inspector);
+ String pkg = "lambdas_kstyle_generics";
+ String grpPkg = allowAccessModification ? "" : pkg;
- verifier.assertLambdaGroups(
- kstyle(grpPkg, 1, 3), // Group for Any
- kstyle(grpPkg, "L", 1), // Group for Beta
- kstyle(grpPkg, "LS", 1), // Group for Gamma
- kstyle(grpPkg, 1, 2) // Group for int
- );
+ verifier.assertLambdaGroups(
+ kstyle(grpPkg, 1, 3), // Group for Any
+ kstyle(grpPkg, "L", 1), // Group for Beta
+ kstyle(grpPkg, "LS", 1), // Group for Gamma
+ kstyle(grpPkg, 1, 2) // Group for int
+ );
- verifier.assertLambdas(new Lambda(pkg, "MainKt$main$4", 1));
- });
+ verifier.assertLambdas(new Lambda(pkg, "MainKt$main$4", 1));
+ });
}
@Test
public void testInnerClassesAndEnclosingMethodsKs() throws Exception {
final String mainClassName = "lambdas_kstyle_generics.MainKt";
runTest(
- "lambdas_kstyle_generics",
- mainClassName,
- KEEP_INNER_AND_ENCLOSING,
- getOptionsModifier(),
- app -> {
- if (enableUnusedInterfaceRemoval) {
- // Only test that the code generates the same output as the input code does on the JVM.
- return;
- }
+ "lambdas_kstyle_generics",
+ mainClassName,
+ testBuilder ->
+ testBuilder
+ .addKeepAttributeInnerClassesAndEnclosingMethod()
+ .addOptionsModification(this::configure))
+ .inspect(
+ inspector -> {
+ if (enableUnusedInterfaceRemoval) {
+ // Only test that the code generates the same output as the input code does on the
+ // JVM.
+ return;
+ }
- Verifier verifier = new Verifier(app);
- String pkg = "lambdas_kstyle_generics";
- String grpPkg = allowAccessModification ? "" : pkg;
+ Verifier verifier = new Verifier(inspector);
+ String pkg = "lambdas_kstyle_generics";
+ String grpPkg = allowAccessModification ? "" : pkg;
- verifier.assertLambdaGroups(
- kstyle(grpPkg, 1, 3), // Group for Any
- kstyle(grpPkg, "L", 1), // Group for Beta // First
- kstyle(grpPkg, "L", 1), // Group for Beta // Second
- kstyle(grpPkg, "LS", 1), // Group for Gamma // First
- kstyle(grpPkg, "LS", 1), // Group for Gamma // Second
- kstyle(grpPkg, 1, 2) // Group for int
- );
+ verifier.assertLambdaGroups(
+ kstyle(grpPkg, 1, 3), // Group for Any
+ kstyle(grpPkg, "L", 1), // Group for Beta // First
+ kstyle(grpPkg, "L", 1), // Group for Beta // Second
+ kstyle(grpPkg, "LS", 1), // Group for Gamma // First
+ kstyle(grpPkg, "LS", 1), // Group for Gamma // Second
+ kstyle(grpPkg, 1, 2) // Group for int
+ );
- verifier.assertLambdas(new Lambda(pkg, "MainKt$main$4", 1));
- });
+ verifier.assertLambdas(new Lambda(pkg, "MainKt$main$4", 1));
+ });
}
@Test
public void testGenericsSignatureInnerEnclosingKs() throws Exception {
final String mainClassName = "lambdas_kstyle_generics.MainKt";
runTest(
- "lambdas_kstyle_generics",
- mainClassName,
- KEEP_SIGNATURE_INNER_ENCLOSING,
- getOptionsModifier(),
- app -> {
- if (enableUnusedInterfaceRemoval) {
- // Only test that the code generates the same output as the input code does on the JVM.
- return;
- }
+ "lambdas_kstyle_generics",
+ mainClassName,
+ // KEEP_SIGNATURE_INNER_ENCLOSING,
+ testBuilder ->
+ testBuilder
+ .addKeepAttributeInnerClassesAndEnclosingMethod()
+ .addKeepAttributeSignature()
+ .addOptionsModification(this::configure))
+ .inspect(
+ inspector -> {
+ if (enableUnusedInterfaceRemoval) {
+ // Only test that the code generates the same output as the input code does on the
+ // JVM.
+ return;
+ }
- Verifier verifier = new Verifier(app);
- String pkg = "lambdas_kstyle_generics";
- String grpPkg = allowAccessModification ? "" : pkg;
+ Verifier verifier = new Verifier(inspector);
+ String pkg = "lambdas_kstyle_generics";
+ String grpPkg = allowAccessModification ? "" : pkg;
- verifier.assertLambdaGroups(
- kstyle(grpPkg, 1, 3), // Group for Any
- kstyle(grpPkg, "L", 1), // Group for Beta in First
- kstyle(grpPkg, "L", 1), // Group for Beta in Second
- kstyle(grpPkg, "LS", 1), // Group for Gamma<String> in First
- kstyle(grpPkg, "LS", 1), // Group for Gamma<Integer> in First
- kstyle(grpPkg, "LS", 1), // Group for Gamma<String> in Second
- kstyle(grpPkg, "LS", 1), // Group for Gamma<Integer> in Second
- kstyle(grpPkg, 1, 2) // Group for int
- );
+ verifier.assertLambdaGroups(
+ kstyle(grpPkg, 1, 3), // Group for Any
+ kstyle(grpPkg, "L", 1), // Group for Beta in First
+ kstyle(grpPkg, "L", 1), // Group for Beta in Second
+ kstyle(grpPkg, "LS", 1), // Group for Gamma<String> in First
+ kstyle(grpPkg, "LS", 1), // Group for Gamma<Integer> in First
+ kstyle(grpPkg, "LS", 1), // Group for Gamma<String> in Second
+ kstyle(grpPkg, "LS", 1), // Group for Gamma<Integer> in Second
+ kstyle(grpPkg, 1, 2) // Group for int
+ );
- verifier.assertLambdas(new Lambda(pkg, "MainKt$main$4", 1));
- });
+ verifier.assertLambdas(new Lambda(pkg, "MainKt$main$4", 1));
+ });
}
@Test
public void testTrivialJs() throws Exception {
final String mainClassName = "lambdas_jstyle_trivial.MainKt";
runTest(
- "lambdas_jstyle_trivial",
- mainClassName,
- getOptionsModifier(),
- app -> {
- Verifier verifier = new Verifier(app);
- String pkg = "lambdas_jstyle_trivial";
- String grp = allowAccessModification ? "" : pkg;
+ "lambdas_jstyle_trivial",
+ mainClassName,
+ testBuilder -> testBuilder.addOptionsModification(this::configure))
+ .inspect(
+ inspector -> {
+ Verifier verifier = new Verifier(inspector);
+ String pkg = "lambdas_jstyle_trivial";
+ String grp = allowAccessModification ? "" : pkg;
- String supplier = "lambdas_jstyle_trivial.Lambdas$Supplier";
- String intSupplier = "lambdas_jstyle_trivial.Lambdas$IntSupplier";
- String consumer = "lambdas_jstyle_trivial.Lambdas$Consumer";
- String intConsumer = "lambdas_jstyle_trivial.Lambdas$IntConsumer";
- String multiFunction = "lambdas_jstyle_trivial.Lambdas$MultiFunction";
+ String supplier = "lambdas_jstyle_trivial.Lambdas$Supplier";
+ String intSupplier = "lambdas_jstyle_trivial.Lambdas$IntSupplier";
+ String consumer = "lambdas_jstyle_trivial.Lambdas$Consumer";
+ String intConsumer = "lambdas_jstyle_trivial.Lambdas$IntConsumer";
+ String multiFunction = "lambdas_jstyle_trivial.Lambdas$MultiFunction";
- verifier.assertLambdaGroups(
- jstyle(grp, 0, intSupplier, 2),
- jstyle(grp, "L", 0, supplier),
- jstyle(grp, "LL", 0, supplier),
- jstyle(grp, "LLL", 0, supplier),
- jstyle(grp, 1, intConsumer, allowAccessModification ? 3 : 2),
- jstyle(grp, "I", 1, consumer),
- jstyle(grp, "II", 1, consumer),
- jstyle(grp, "III", 1, consumer),
- jstyle(grp, "IIII", 1, consumer),
- jstyle(grp, 3, multiFunction, 2),
- jstyle(grp, 3, multiFunction, 2),
- jstyle(grp, 3, multiFunction, 4),
- jstyle(grp, 3, multiFunction, 6));
+ verifier.assertLambdaGroups(
+ jstyle(grp, 0, intSupplier, 2),
+ jstyle(grp, "L", 0, supplier),
+ jstyle(grp, "LL", 0, supplier),
+ jstyle(grp, "LLL", 0, supplier),
+ jstyle(grp, 1, intConsumer, allowAccessModification ? 3 : 2),
+ jstyle(grp, "I", 1, consumer),
+ jstyle(grp, "II", 1, consumer),
+ jstyle(grp, "III", 1, consumer),
+ jstyle(grp, "IIII", 1, consumer),
+ jstyle(grp, 3, multiFunction, 2),
+ jstyle(grp, 3, multiFunction, 2),
+ jstyle(grp, 3, multiFunction, 4),
+ jstyle(grp, 3, multiFunction, 6));
- verifier.assertLambdas(
- allowAccessModification
- ? new Lambda[] {
- new Lambda(pkg + "/inner", "InnerKt$testInner1$4", 1),
- new Lambda(pkg + "/inner", "InnerKt$testInner1$5", 1)
- }
- : new Lambda[] {
- new Lambda(pkg + "/inner", "InnerKt$testInner1$1", 1),
- new Lambda(pkg + "/inner", "InnerKt$testInner1$2", 1),
- new Lambda(pkg + "/inner", "InnerKt$testInner1$3", 1),
- new Lambda(pkg + "/inner", "InnerKt$testInner1$4", 1),
- new Lambda(pkg + "/inner", "InnerKt$testInner1$5", 1)
- });
- });
+ verifier.assertLambdas(
+ allowAccessModification
+ ? new Lambda[] {
+ new Lambda(pkg + "/inner", "InnerKt$testInner1$4", 1),
+ new Lambda(pkg + "/inner", "InnerKt$testInner1$5", 1)
+ }
+ : new Lambda[] {
+ new Lambda(pkg + "/inner", "InnerKt$testInner1$1", 1),
+ new Lambda(pkg + "/inner", "InnerKt$testInner1$2", 1),
+ new Lambda(pkg + "/inner", "InnerKt$testInner1$3", 1),
+ new Lambda(pkg + "/inner", "InnerKt$testInner1$4", 1),
+ new Lambda(pkg + "/inner", "InnerKt$testInner1$5", 1)
+ });
+ });
}
@Test
public void testSingleton() throws Exception {
final String mainClassName = "lambdas_singleton.MainKt";
runTest(
- "lambdas_singleton",
- mainClassName,
- "-nohorizontalclassmerging class *",
- getOptionsModifier(),
- app -> {
- Verifier verifier = new Verifier(app);
- String pkg = "lambdas_singleton";
- String grp = allowAccessModification ? "" : pkg;
+ "lambdas_singleton",
+ mainClassName,
+ testBuilder ->
+ testBuilder.addOptionsModification(this::configure).noHorizontalClassMerging())
+ .inspect(
+ inspector -> {
+ Verifier verifier = new Verifier(inspector);
+ String pkg = "lambdas_singleton";
+ String grp = allowAccessModification ? "" : pkg;
- verifier.assertLambdaGroups(
- kstyle(grp, 1, 1 /* 1 out of 5 lambdas in the group */),
- jstyle(grp, 2, "java.util.Comparator", 0 /* 0 out of 2 lambdas in the group */));
+ verifier.assertLambdaGroups(
+ kstyle(grp, 1, 1 /* 1 out of 5 lambdas in the group */),
+ jstyle(grp, 2, "java.util.Comparator", 0 /* 0 out of 2 lambdas in the group */));
- verifier.assertLambdas(/* None */ );
- });
+ verifier.assertLambdas(/* None */ );
+ });
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java
index 5ba846c..d6e8c53 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java
@@ -6,21 +6,13 @@
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.InternalOptions;
import java.util.Collection;
-import java.util.function.Consumer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class KotlinLambdaMergingWithReprocessingTest extends AbstractR8KotlinTestBase {
- private Consumer<InternalOptions> optionsModifier =
- o -> {
- o.enableInlining = true;
- o.enableClassInlining = true;
- o.enableLambdaMerging = true;
- };
@Parameterized.Parameters(name = "target: {0}, allowAccessModification: {1}")
public static Collection<Object[]> data() {
@@ -35,6 +27,15 @@
@Test
public void testMergingKStyleLambdasAndReprocessing() throws Exception {
final String mainClassName = "reprocess_merged_lambdas_kstyle.MainKt";
- runTest("reprocess_merged_lambdas_kstyle", mainClassName, optionsModifier, null);
+ runTest(
+ "reprocess_merged_lambdas_kstyle",
+ mainClassName,
+ testBuilder ->
+ testBuilder.addOptionsModification(
+ options -> {
+ options.enableInlining = true;
+ options.enableClassInlining = true;
+ options.enableLambdaMerging = true;
+ }));
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
index 446d982..127c1e3 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
@@ -6,22 +6,13 @@
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.InternalOptions;
import java.util.Collection;
-import java.util.function.Consumer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class KotlinLambdaMergingWithSmallInliningBudgetTest extends AbstractR8KotlinTestBase {
- private Consumer<InternalOptions> optionsModifier =
- o -> {
- o.enableInlining = true;
- o.enableClassInlining = true;
- o.enableLambdaMerging = true;
- o.inliningInstructionAllowance = 3;
- };
@Parameterized.Parameters(name = "target: {0}, allowAccessModification: {1}")
public static Collection<Object[]> data() {
@@ -36,6 +27,11 @@
@Test
public void testJStyleRunnable() throws Exception {
final String mainClassName = "lambdas_jstyle_runnable.MainKt";
- runTest("lambdas_jstyle_runnable", mainClassName, optionsModifier, null);
+ runTest(
+ "lambdas_jstyle_runnable",
+ mainClassName,
+ testBuilder ->
+ testBuilder.addOptionsModification(
+ options -> options.inliningInstructionAllowance = 3));
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java
index a06dcf8..d764d5f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java
@@ -125,6 +125,8 @@
.addKeepClassAndMembersRules(baseClassName)
.addKeepClassAndMembersRules(featureKtClassNamet)
.addKeepClassAndMembersRules(FeatureAPI.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMergingOfKotlinLambdas = false)
.setMinApi(parameters.getApiLevel())
.noMinification() // The check cast inspection above relies on original names.
.addFeatureSplit(
diff --git a/src/test/java/com/android/tools/r8/naming/FieldNamingObfuscationDictionaryTest.java b/src/test/java/com/android/tools/r8/naming/FieldNamingObfuscationDictionaryTest.java
index be7230d..0ab866f 100644
--- a/src/test/java/com/android/tools/r8/naming/FieldNamingObfuscationDictionaryTest.java
+++ b/src/test/java/com/android/tools/r8/naming/FieldNamingObfuscationDictionaryTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -51,6 +52,7 @@
}
@NeverClassInline
+ @NoHorizontalClassMerging
public static class C extends A {
public int f0;
@@ -98,6 +100,7 @@
.addKeepRules("-overloadaggressively", "-obfuscationdictionary " + dictionary.toString())
.addKeepMainRule(Runner.class)
.enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Runner.class, "HELLO", "WORLD")
diff --git a/src/test/java/com/android/tools/r8/naming/InterfaceMethodNameMinifierTest.java b/src/test/java/com/android/tools/r8/naming/InterfaceMethodNameMinifierTest.java
index f2e1a60..a853b95 100644
--- a/src/test/java/com/android/tools/r8/naming/InterfaceMethodNameMinifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/InterfaceMethodNameMinifierTest.java
@@ -27,7 +27,7 @@
"-obfuscationdictionary " + dictionary.toString())
// Minify the interface methods in alphabetic order.
.addOptionsModification(
- options -> options.testing.minifier.interfaceMethodOrdering = DexMethod::slowCompareTo)
+ options -> options.testing.minifier.interfaceMethodOrdering = DexMethod::compareTo)
.compile();
}
diff --git a/src/test/java/com/android/tools/r8/naming/ProguardMapReaderPackageInfoTest.java b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderPackageInfoTest.java
new file mode 100644
index 0000000..c889dbb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderPackageInfoTest.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.IOException;
+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 ProguardMapReaderPackageInfoTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final String MAPPING =
+ StringUtils.joinLines(
+ "android.support.v4.internal.package-info -> android.support.v4.internal.package-info:",
+ "# {\"id\":\"sourceFile\",\"fileName\":\"SourceFile\"}");
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public ProguardMapReaderPackageInfoTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void parseMappings() throws IOException {
+ ClassNameMapper.mapperFromString(MAPPING);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
index 1ab9fe1..668a8a0 100644
--- a/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
@@ -26,7 +26,7 @@
private static final String ROOT = ToolHelper.EXAMPLES_BUILD_DIR;
private static final String EXAMPLE_MAP = "throwing/throwing.map";
private static final String EXAMPLE_MAP_WITH_PACKAGE_INFO =
- "dagger.android.package-info -> dagger.android.package-info\n";
+ "dagger.android.package-info -> dagger.android.package-info:\n";
@Test
public void parseThrowingMap() throws IOException {
@@ -141,7 +141,7 @@
@Test
public void parseMapWithPackageInfo() throws IOException {
ClassNameMapper mapper = ClassNameMapper.mapperFromString(EXAMPLE_MAP_WITH_PACKAGE_INFO);
- Assert.assertTrue(mapper.getObfuscatedToOriginalMapping().original.isEmpty());
+ assertEquals(1, mapper.getObfuscatedToOriginalMapping().original.size());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/naming/identifiernamestring/ClassNameComparisonTest.java b/src/test/java/com/android/tools/r8/naming/identifiernamestring/ClassNameComparisonTest.java
index 5aaf063..f37c70c 100644
--- a/src/test/java/com/android/tools/r8/naming/identifiernamestring/ClassNameComparisonTest.java
+++ b/src/test/java/com/android/tools/r8/naming/identifiernamestring/ClassNameComparisonTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -21,7 +22,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public ClassNameComparisonTest(TestParameters parameters) {
@@ -33,7 +34,8 @@
testForR8(parameters.getBackend())
.addInnerClasses(ClassNameComparisonTest.class)
.addKeepMainRule(TestClass.class)
- .setMinApi(parameters.getRuntime())
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutputLines("Hello!", "Hello " + B.class.getName() + "!");
@@ -64,7 +66,9 @@
}
}
+ @NoHorizontalClassMerging
static class A {}
+ @NoHorizontalClassMerging
static class B {}
}
diff --git a/src/test/java/com/android/tools/r8/peephole/suffixsharing/IdenticalBlockSuffixSharingWithArrayTypesTest.java b/src/test/java/com/android/tools/r8/peephole/suffixsharing/IdenticalBlockSuffixSharingWithArrayTypesTest.java
index 1781d9f..86fd5d0 100644
--- a/src/test/java/com/android/tools/r8/peephole/suffixsharing/IdenticalBlockSuffixSharingWithArrayTypesTest.java
+++ b/src/test/java/com/android/tools/r8/peephole/suffixsharing/IdenticalBlockSuffixSharingWithArrayTypesTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.StringUtils;
@@ -54,7 +55,7 @@
new ClassTestParameter(InstancePutTestClass.class),
new ClassTestParameter(InvokeTestClass.class),
new ClassTestParameter(StaticPutTestClass.class)),
- getTestParameters().withAllRuntimes().build());
+ getTestParameters().withAllRuntimesAndApiLevels().build());
}
public IdenticalBlockSuffixSharingWithArrayTypesTest(
@@ -70,7 +71,7 @@
String expectedOutput = StringUtils.lines("42");
testForD8()
.addInnerClasses(IdenticalBlockSuffixSharingWithArrayTypesTest.class)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::verifyInstructionCount)
.run(parameters.getRuntime(), clazz)
@@ -85,7 +86,8 @@
.addKeepMainRule(clazz)
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::verifyInstructionCount)
.run(parameters.getRuntime(), clazz)
@@ -233,7 +235,9 @@
interface K extends I {}
+ @NoHorizontalClassMerging
class A implements I {}
+ @NoHorizontalClassMerging
class B implements I {}
}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageAnnotationTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageAnnotationTest.java
new file mode 100644
index 0000000..61526d4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageAnnotationTest.java
@@ -0,0 +1,83 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.repackage;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageAnnotationTest extends RepackageTestBase {
+
+ private final String EXPECTED = "Hello World";
+
+ public RepackageAnnotationTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(Main.class, Annotation.class, A.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, A.class, Annotation.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .addKeepRuntimeVisibleAnnotations()
+ .addKeepRules(
+ "-keep,allowobfuscation @interface " + Annotation.class.getTypeName() + " {",
+ " *;",
+ "}")
+ .apply(this::configureRepackaging)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(new A().getAnnotationValues());
+ }
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.TYPE})
+ public @interface Annotation {
+
+ String f1();
+
+ String f2();
+ }
+
+ @Annotation(f1 = "Hello", f2 = "World")
+ @NeverClassInline
+ public static class A {
+
+ @NeverInline
+ public String getAnnotationValues() {
+ Annotation annotation = A.class.getAnnotation(Annotation.class);
+ if (annotation == null) {
+ return null;
+ }
+ return annotation.f1() + " " + annotation.f2();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageInnerAndOuterClassTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageInnerAndOuterClassTest.java
index 47a1dcd..81de378 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageInnerAndOuterClassTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageInnerAndOuterClassTest.java
@@ -7,6 +7,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoStaticClassMerging;
import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestParameters;
@@ -46,6 +47,7 @@
.addKeepAttributeInnerClassesAndEnclosingMethod()
.apply(this::configureRepackaging)
.enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.enableNoStaticClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
@@ -67,6 +69,7 @@
}
}
+ @NoHorizontalClassMerging
@NoStaticClassMerging
public static class Outer {
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageTest.java
index 150a7cc..da09e88 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageTest.java
@@ -112,6 +112,7 @@
.allowAccessModification(allowAccessModification)
.apply(this::configureRepackaging)
.enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.enableNoStaticClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassDirect.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassDirect.java
index 4f47751..6ee9df6 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassDirect.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassDirect.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.repackage.testclasses.repackagetest;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoStaticClassMerging;
+@NoHorizontalClassMerging
@NoStaticClassMerging
public class AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassDirect {
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassIndirect.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassIndirect.java
index fdc4a9c..7a80bb3 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassIndirect.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassIndirect.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.repackage.testclasses.repackagetest;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoStaticClassMerging;
+@NoHorizontalClassMerging
@NoStaticClassMerging
public class AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassIndirect {
@@ -15,6 +17,7 @@
Helper.test();
}
+ @NoHorizontalClassMerging
@NoStaticClassMerging
public static class Helper {
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodOnReachableClassDirect.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodOnReachableClassDirect.java
index 0f9ae96..f8af76d 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodOnReachableClassDirect.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodOnReachableClassDirect.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.repackage.testclasses.repackagetest;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoStaticClassMerging;
+@NoHorizontalClassMerging
@NoStaticClassMerging
public class AccessPackagePrivateKeptMethodOnReachableClassDirect {
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodOnReachableClassIndirect.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodOnReachableClassIndirect.java
index 68aa391..279d85b 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodOnReachableClassIndirect.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateKeptMethodOnReachableClassIndirect.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.repackage.testclasses.repackagetest;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoStaticClassMerging;
+@NoHorizontalClassMerging
@NoStaticClassMerging
public class AccessPackagePrivateKeptMethodOnReachableClassIndirect {
@@ -15,6 +17,7 @@
Helper.test();
}
+ @NoHorizontalClassMerging
@NoStaticClassMerging
public static class Helper {
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassAllowRenamingDirect.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassAllowRenamingDirect.java
index de0e859..a333df7 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassAllowRenamingDirect.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassAllowRenamingDirect.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.repackage.testclasses.repackagetest;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoStaticClassMerging;
+@NoHorizontalClassMerging
@NoStaticClassMerging
public class AccessPackagePrivateMethodOnKeptClassAllowRenamingDirect {
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassAllowRenamingIndirect.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassAllowRenamingIndirect.java
index 13fef6e..040dd72 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassAllowRenamingIndirect.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassAllowRenamingIndirect.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.repackage.testclasses.repackagetest;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoStaticClassMerging;
+@NoHorizontalClassMerging
@NoStaticClassMerging
public class AccessPackagePrivateMethodOnKeptClassAllowRenamingIndirect {
@@ -15,6 +17,7 @@
Helper.test();
}
+ @NoHorizontalClassMerging
@NoStaticClassMerging
public static class Helper {
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassDirect.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassDirect.java
index fc92f85..a7bb32f 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassDirect.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassDirect.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.repackage.testclasses.repackagetest;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoStaticClassMerging;
+@NoHorizontalClassMerging
@NoStaticClassMerging
public class AccessPackagePrivateMethodOnKeptClassDirect {
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassIndirect.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassIndirect.java
index c30cd59..2319ca4 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassIndirect.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnKeptClassIndirect.java
@@ -5,9 +5,11 @@
package com.android.tools.r8.repackage.testclasses.repackagetest;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoStaticClassMerging;
import com.android.tools.r8.NoVerticalClassMerging;
+@NoHorizontalClassMerging
@NoStaticClassMerging
public class AccessPackagePrivateMethodOnKeptClassIndirect {
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnReachableClassDirect.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnReachableClassDirect.java
index 73f2d6e..07872c6b 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnReachableClassDirect.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnReachableClassDirect.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.repackage.testclasses.repackagetest;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoStaticClassMerging;
+@NoHorizontalClassMerging
@NoStaticClassMerging
public class AccessPackagePrivateMethodOnReachableClassDirect {
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnReachableClassIndirect.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnReachableClassIndirect.java
index 54cbea9..d528244 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnReachableClassIndirect.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPackagePrivateMethodOnReachableClassIndirect.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.repackage.testclasses.repackagetest;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoStaticClassMerging;
+@NoHorizontalClassMerging
@NoStaticClassMerging
public class AccessPackagePrivateMethodOnReachableClassIndirect {
@@ -15,6 +17,7 @@
Helper.test();
}
+ @NoHorizontalClassMerging
@NoStaticClassMerging
public static class Helper {
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicKeptMethodAllowRenamingOnReachableClass.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicKeptMethodAllowRenamingOnReachableClass.java
index cfb5aa4..d99a9b5 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicKeptMethodAllowRenamingOnReachableClass.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicKeptMethodAllowRenamingOnReachableClass.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.repackage.testclasses.repackagetest;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoStaticClassMerging;
+@NoHorizontalClassMerging
@NoStaticClassMerging
public class AccessPublicKeptMethodAllowRenamingOnReachableClass {
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicKeptMethodOnReachableClass.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicKeptMethodOnReachableClass.java
index 75d353c..8880ca5 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicKeptMethodOnReachableClass.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicKeptMethodOnReachableClass.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.repackage.testclasses.repackagetest;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoStaticClassMerging;
+@NoHorizontalClassMerging
@NoStaticClassMerging
public class AccessPublicKeptMethodOnReachableClass {
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnKeptClass.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnKeptClass.java
index b77f419..d3fa44b 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnKeptClass.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnKeptClass.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.repackage.testclasses.repackagetest;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoStaticClassMerging;
+@NoHorizontalClassMerging
@NoStaticClassMerging
public class AccessPublicMethodOnKeptClass {
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnKeptClassAllowRenaming.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnKeptClassAllowRenaming.java
index f71b585..9578cc7 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnKeptClassAllowRenaming.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnKeptClassAllowRenaming.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.repackage.testclasses.repackagetest;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoStaticClassMerging;
+@NoHorizontalClassMerging
@NoStaticClassMerging
public class AccessPublicMethodOnKeptClassAllowRenaming {
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnReachableClass.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnReachableClass.java
index 87ed71f..ec21ae7 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnReachableClass.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/AccessPublicMethodOnReachableClass.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.repackage.testclasses.repackagetest;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoStaticClassMerging;
+@NoHorizontalClassMerging
@NoStaticClassMerging
public class AccessPublicMethodOnReachableClass {
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/ReachableClass.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/ReachableClass.java
index 10f12bf..22c5aa0 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/ReachableClass.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagetest/ReachableClass.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.repackage.testclasses.repackagetest;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoStaticClassMerging;
+@NoHorizontalClassMerging
@NoStaticClassMerging
public class ReachableClass {
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/EnumInvalidValuesLengthTest.java b/src/test/java/com/android/tools/r8/rewrite/enums/EnumInvalidValuesLengthTest.java
new file mode 100644
index 0000000..ead99d1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/EnumInvalidValuesLengthTest.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.rewrite.enums;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.transformers.MethodTransformer;
+import java.io.IOException;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class EnumInvalidValuesLengthTest extends TestBase {
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public EnumInvalidValuesLengthTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testValuesLength() throws Exception {
+ Assume.assumeTrue(
+ "TODO(b/172903562): Breaks on dex due to enum unboxing", parameters.isCfRuntime());
+ testForR8(parameters.getBackend())
+ .addKeepMainRule(EnumInvalidValuesLengthTest.Main.class)
+ .addProgramClasses(EnumInvalidValuesLengthTest.Main.class)
+ .addProgramClassFileData(transformValues(MyEnum.class))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), EnumInvalidValuesLengthTest.Main.class)
+ .assertSuccessWithOutputLines("5");
+ }
+
+ private byte[] transformValues(Class<MyEnum> myEnumClass) throws IOException {
+ return transformer(myEnumClass)
+ .addMethodTransformer(
+ new MethodTransformer() {
+ @Override
+ public void visitInsn(int opcode) {
+ if (opcode == Opcodes.ICONST_3) {
+ // This is the constant determining the size of the values array.
+ super.visitInsn(Opcodes.ICONST_5);
+ return;
+ }
+ super.visitInsn(opcode);
+ }
+ })
+ .transform();
+ }
+
+ @NeverClassInline
+ enum MyEnum {
+ A,
+ B,
+ C;
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ System.out.println(MyEnum.values().length);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/EnumValuesLengthTest.java b/src/test/java/com/android/tools/r8/rewrite/enums/EnumValuesLengthTest.java
deleted file mode 100644
index 176e6f5..0000000
--- a/src/test/java/com/android/tools/r8/rewrite/enums/EnumValuesLengthTest.java
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.rewrite.enums;
-
-import static junit.framework.TestCase.assertTrue;
-
-import com.android.tools.r8.NeverClassInline;
-import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper;
-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.InstructionSubject;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.Collections;
-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 EnumValuesLengthTest extends TestBase {
-
- private final TestParameters parameters;
-
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
- }
-
- public EnumValuesLengthTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
- @Test
- public void testValuesLengthRemoved() throws Exception {
- testForR8(parameters.getBackend())
- .addKeepMainRule(Main.class)
- .addInnerClasses(EnumValuesLengthTest.class)
- .noMinification()
- .setMinApi(parameters.getApiLevel())
- .addOptionsModification(
- opt -> {
- opt.enableEnumValueOptimization = true;
- // We need to keep the switch map to ensure kept switch maps have their
- // values array length rewritten.
- opt.enableEnumSwitchMapRemoval = false;
- })
- .compile()
- .inspect(this::assertValuesLengthRemoved)
- .run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("0", "2", "5", "a", "D", "c", "D");
- }
-
- @Test
- public void testValuesLengthSwitchMapRemoved() throws Exception {
- // Make sure SwitchMap can still be removed with valuesLength optimization.
- assertSwitchMapPresent();
- testForR8(parameters.getBackend())
- .addKeepMainRule(Main.class)
- .addInnerClasses(EnumValuesLengthTest.class)
- .noMinification()
- .setMinApi(parameters.getApiLevel())
- .addOptionsModification(
- opt -> {
- opt.enableEnumValueOptimization = true;
- })
- .compile()
- .inspect(this::assertSwitchMapRemoved)
- .run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("0", "2", "5", "a", "D", "c", "D");
- }
-
- private void assertSwitchMapPresent() throws IOException {
- Collection<Path> classFilesForInnerClasses =
- ToolHelper.getClassFilesForInnerClasses(
- Collections.singletonList(EnumValuesLengthTest.class));
- assertTrue(classFilesForInnerClasses.stream().anyMatch(p -> p.toString().endsWith("$1.class")));
- }
-
- private void assertSwitchMapRemoved(CodeInspector inspector) {
- assertTrue(inspector.allClasses().stream().noneMatch(c -> c.getOriginalName().endsWith("$1")));
- }
-
- private void assertValuesLengthRemoved(CodeInspector inspector) {
- for (FoundClassSubject clazz : inspector.allClasses()) {
- clazz.forAllMethods(this::assertValuesLengthRemoved);
- }
- }
-
- private void assertValuesLengthRemoved(FoundMethodSubject method) {
- assertTrue(method.streamInstructions().noneMatch(InstructionSubject::isArrayLength));
- assertTrue(
- method
- .streamInstructions()
- .noneMatch(
- instr ->
- instr.isInvokeStatic() && instr.getMethod().name.toString().equals("values")));
- }
-
- public static class Main {
-
- @NeverClassInline
- enum E0 {}
-
- @NeverClassInline
- enum E2 {
- A,
- B
- }
-
- @NeverClassInline
- enum E5 {
- A,
- B,
- C,
- D,
- E
- }
-
- @NeverClassInline
- enum EUnusedValues {
- A,
- B,
- C
- }
-
- @NeverClassInline
- enum ESwitch {
- A,
- B,
- C,
- D
- }
-
- @SuppressWarnings("ResultOfMethodCallIgnored")
- public static void main(String[] args) {
- EUnusedValues.values();
- System.out.println(E0.values().length);
- System.out.println(E2.values().length);
- System.out.println(E5.values().length);
- System.out.println(switchOn(ESwitch.A));
- System.out.println(switchOn(ESwitch.B));
- System.out.println(switchOn(ESwitch.C));
- System.out.println(switchOn(ESwitch.D));
- }
-
- // SwitchMaps feature an array length on values, and some of them are not removed.
- @NeverInline
- static char switchOn(ESwitch e) {
- switch (e) {
- case A:
- return 'a';
- case C:
- return 'c';
- default:
- return 'D';
- }
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java b/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java
index 745e5b7..d408a4f 100644
--- a/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/EffectivelyFinalInstanceFieldsTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -54,6 +55,7 @@
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoVerticalClassMergingAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getRuntime())
.compile()
.inspect(
@@ -155,6 +157,7 @@
@NeverClassInline
@NoVerticalClassMerging
+ @NoHorizontalClassMerging
static class InstanceFieldWithInitialization_Z {
boolean alwaysFalse;
InstanceFieldWithInitialization_Z() {
@@ -163,6 +166,7 @@
}
@NeverClassInline
+ @NoHorizontalClassMerging
static class InstanceFieldWithNonTrivialInitialization_Z
extends InstanceFieldWithInitialization_Z {
boolean alwaysTrue;
@@ -182,6 +186,7 @@
}
@NeverClassInline
+ @NoHorizontalClassMerging
static class InstanceFieldWithInitialization_I {
int alwaysZero;
InstanceFieldWithInitialization_I() {
@@ -190,6 +195,7 @@
}
@NeverClassInline
+ @NoHorizontalClassMerging
static class InstanceFieldWithRange_I {
int alwaysLessThanEight;
InstanceFieldWithRange_I() {
@@ -215,6 +221,7 @@
}
@NeverClassInline
+ @NoHorizontalClassMerging
static class InstanceFieldWithInitialization_L {
Object alwaysNull;
InstanceFieldWithInitialization_L() {
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 fd676a3..b5aba75 100644
--- a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
@@ -248,7 +248,7 @@
.compile()
.graphInspector();
// TODO(b/159418523): Should the reference be equal to the result with the conditional rule?
- assertRetainedClassesEqual(referenceInspector, ifHasMemberThenKeepClassInspector, true, false);
+ assertRetainedClassesEqual(referenceInspector, ifHasMemberThenKeepClassInspector, true, true);
}
private void assertRetainedClassesEqual(
diff --git a/src/test/java/com/android/tools/r8/shaking/attributes/KeepSignatureTest.java b/src/test/java/com/android/tools/r8/shaking/attributes/KeepSignatureTest.java
index 9dfff6e..ef2e860 100644
--- a/src/test/java/com/android/tools/r8/shaking/attributes/KeepSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/attributes/KeepSignatureTest.java
@@ -56,7 +56,8 @@
@Test
public void testKeptClassFieldAndMethodFull() throws Exception {
- runTest(testForR8(parameters.getBackend()), false);
+ // TODO(b/172999267): The below should be true
+ runTest(testForR8(parameters.getBackend()), true);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java b/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java
index 23a5616..8bde97b 100644
--- a/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.shaking.b134858535;
-import static org.hamcrest.CoreMatchers.containsString;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
@@ -28,13 +27,9 @@
.addProgramClassFileData(EventPublisher$bDump.dump())
.addKeepClassRules(Interface.class)
.addKeepMainRule(Main.class)
- .allowDiagnosticInfoMessages()
.setMinApi(AndroidApiLevel.L)
- .compile()
- // TODO(b/157537996): Handle JStyle lambdas with private methods.
- .assertAllInfoMessagesMatch(
- containsString(
- "Unrecognized Kotlin lambda"
- + " [com.android.tools.r8.shaking.b134858535.EventPublisher$b]"));
+ .addHorizontallyMergedLambdaClassesInspector(
+ inspector -> inspector.assertClassNotMerged(EventPublisher$b.class))
+ .compile();
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByInvokeStaticOnSubInterfaceTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByInvokeStaticOnSubInterfaceTest.java
index 943e680..fe71482 100644
--- a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByInvokeStaticOnSubInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByInvokeStaticOnSubInterfaceTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import org.junit.Test;
@@ -61,7 +62,11 @@
@Test
public void testClassInitializationMayHaveSideEffects() throws Exception {
AppView<AppInfoWithLiveness> appView =
- computeAppViewWithLiveness(buildInnerClasses(getClass()).build(), TestClass.class);
+ computeAppViewWithLiveness(
+ buildInnerClasses(getClass())
+ .addLibraryFile(ToolHelper.getMostRecentAndroidJar())
+ .build(),
+ TestClass.class);
assertMayHaveClassInitializationSideEffects(appView, J.class);
}
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByInvokeStaticTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByInvokeStaticTest.java
index a052428..e7a460d 100644
--- a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByInvokeStaticTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByInvokeStaticTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import org.junit.Test;
@@ -61,7 +62,11 @@
@Test
public void testClassInitializationMayHaveSideEffects() throws Exception {
AppView<AppInfoWithLiveness> appView =
- computeAppViewWithLiveness(buildInnerClasses(getClass()).build(), TestClass.class);
+ computeAppViewWithLiveness(
+ buildInnerClasses(getClass())
+ .addLibraryFile(ToolHelper.getMostRecentAndroidJar())
+ .build(),
+ TestClass.class);
assertMayHaveClassInitializationSideEffects(appView, I.class);
}
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByStaticGetOnSubClassTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByStaticGetOnSubClassTest.java
index d20f13a..ec1018f 100644
--- a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByStaticGetOnSubClassTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByStaticGetOnSubClassTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import org.junit.Test;
@@ -64,7 +65,11 @@
@Test
public void testClassInitializationMayHaveSideEffects() throws Exception {
AppView<AppInfoWithLiveness> appView =
- computeAppViewWithLiveness(buildInnerClasses(getClass()).build(), TestClass.class);
+ computeAppViewWithLiveness(
+ buildInnerClasses(getClass())
+ .addLibraryFile(ToolHelper.getMostRecentAndroidJar())
+ .build(),
+ TestClass.class);
assertMayHaveClassInitializationSideEffects(appView, A.class);
}
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByStaticGetOnSubInterfaceTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByStaticGetOnSubInterfaceTest.java
index bb59d8c..15b7662 100644
--- a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByStaticGetOnSubInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByStaticGetOnSubInterfaceTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import org.junit.Test;
@@ -64,7 +65,11 @@
@Test
public void testClassInitializationMayHaveSideEffects() throws Exception {
AppView<AppInfoWithLiveness> appView =
- computeAppViewWithLiveness(buildInnerClasses(getClass()).build(), TestClass.class);
+ computeAppViewWithLiveness(
+ buildInnerClasses(getClass())
+ .addLibraryFile(ToolHelper.getMostRecentAndroidJar())
+ .build(),
+ TestClass.class);
assertMayHaveClassInitializationSideEffects(appView, J.class);
}
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByStaticGetTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByStaticGetTest.java
index a3e93ae..65b4bcb 100644
--- a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByStaticGetTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByStaticGetTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import org.junit.Test;
@@ -64,7 +65,11 @@
@Test
public void testClassInitializationMayHaveSideEffects() throws Exception {
AppView<AppInfoWithLiveness> appView =
- computeAppViewWithLiveness(buildInnerClasses(getClass()).build(), TestClass.class);
+ computeAppViewWithLiveness(
+ buildInnerClasses(getClass())
+ .addLibraryFile(ToolHelper.getMostRecentAndroidJar())
+ .build(),
+ TestClass.class);
assertMayHaveClassInitializationSideEffects(appView, I.class);
}
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceNotInitializedByInvokeStaticOnSubClassTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceNotInitializedByInvokeStaticOnSubClassTest.java
index 385b549..d73e875 100644
--- a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceNotInitializedByInvokeStaticOnSubClassTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceNotInitializedByInvokeStaticOnSubClassTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import org.junit.Test;
@@ -66,9 +67,12 @@
@Test
public void testClassInitializationMayHaveSideEffects() throws Exception {
AppView<AppInfoWithLiveness> appView =
- computeAppViewWithLiveness(buildInnerClasses(getClass()).build(), TestClass.class);
- // TODO(b/172049649): Initialization of A does not have side effects.
- assertMayHaveClassInitializationSideEffects(appView, A.class);
+ computeAppViewWithLiveness(
+ buildInnerClasses(getClass())
+ .addLibraryFile(ToolHelper.getMostRecentAndroidJar())
+ .build(),
+ TestClass.class);
+ assertNoClassInitializationSideEffects(appView, A.class);
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceWithDefaultMethodInitializedByInvokeStaticOnSubClassTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceWithDefaultMethodInitializedByInvokeStaticOnSubClassTest.java
index c69a6d0..18ce5f2 100644
--- a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceWithDefaultMethodInitializedByInvokeStaticOnSubClassTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceWithDefaultMethodInitializedByInvokeStaticOnSubClassTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import org.junit.Test;
@@ -80,7 +81,11 @@
@Test
public void testClassInitializationMayHaveSideEffects() throws Exception {
AppView<AppInfoWithLiveness> appView =
- computeAppViewWithLiveness(buildInnerClasses(getClass()).build(), TestClass.class);
+ computeAppViewWithLiveness(
+ buildInnerClasses(getClass())
+ .addLibraryFile(ToolHelper.getMostRecentAndroidJar())
+ .build(),
+ TestClass.class);
assertMayHaveClassInitializationSideEffects(appView, A.class);
}
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceWithDefaultMethodInitializedByInvokeStaticOnSubInterfaceTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceWithDefaultMethodInitializedByInvokeStaticOnSubInterfaceTest.java
new file mode 100644
index 0000000..edcfaf0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceWithDefaultMethodInitializedByInvokeStaticOnSubInterfaceTest.java
@@ -0,0 +1,102 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.clinit;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class InterfaceWithDefaultMethodInitializedByInvokeStaticOnSubInterfaceTest
+ extends ClassMayHaveInitializationSideEffectsTestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public InterfaceWithDefaultMethodInitializedByInvokeStaticOnSubInterfaceTest(
+ TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithEmptyOutput();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .allowStdoutMessages()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithEmptyOutput();
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addTestClasspath()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithEmptyOutput();
+ }
+
+ @Test
+ public void testClassInitializationMayHaveSideEffects() throws Exception {
+ AppView<AppInfoWithLiveness> appView =
+ computeAppViewWithLiveness(
+ buildInnerClasses(getClass())
+ .addLibraryFile(ToolHelper.getMostRecentAndroidJar())
+ .build(),
+ TestClass.class);
+ assertNoClassInitializationSideEffects(appView, J.class);
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ J.greet();
+ }
+ }
+
+ interface I {
+
+ Greeter iGreeter = new Greeter("I");
+
+ default void m() {}
+ }
+
+ interface J extends I {
+
+ static void greet() {}
+ }
+
+ static class Greeter {
+
+ Greeter(String greeting) {
+ System.out.println(greeting);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceWithDefaultMethodInitializedByStaticGetOnSubClassTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceWithDefaultMethodInitializedByStaticGetOnSubClassTest.java
index a31b2ed..96f64f8 100644
--- a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceWithDefaultMethodInitializedByStaticGetOnSubClassTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceWithDefaultMethodInitializedByStaticGetOnSubClassTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import org.junit.Test;
@@ -79,7 +80,11 @@
@Test
public void testClassInitializationMayHaveSideEffects() throws Exception {
AppView<AppInfoWithLiveness> appView =
- computeAppViewWithLiveness(buildInnerClasses(getClass()).build(), TestClass.class);
+ computeAppViewWithLiveness(
+ buildInnerClasses(getClass())
+ .addLibraryFile(ToolHelper.getMostRecentAndroidJar())
+ .build(),
+ TestClass.class);
assertMayHaveClassInitializationSideEffects(appView, A.class);
}
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceWithDefaultMethodInitializedByStaticGetOnSubInterfaceTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceWithDefaultMethodInitializedByStaticGetOnSubInterfaceTest.java
index 1977be8..a751dc3 100644
--- a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceWithDefaultMethodInitializedByStaticGetOnSubInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceWithDefaultMethodInitializedByStaticGetOnSubInterfaceTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import org.junit.Test;
@@ -65,7 +66,11 @@
@Test
public void testClassInitializationMayHaveSideEffects() throws Exception {
AppView<AppInfoWithLiveness> appView =
- computeAppViewWithLiveness(buildInnerClasses(getClass()).build(), TestClass.class);
+ computeAppViewWithLiveness(
+ buildInnerClasses(getClass())
+ .addLibraryFile(ToolHelper.getMostRecentAndroidJar())
+ .build(),
+ TestClass.class);
assertMayHaveClassInitializationSideEffects(appView, J.class);
}
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceWithDefaultMethodNotInitializedByInvokeStaticOnSubInterfaceTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceWithDefaultMethodNotInitializedByInvokeStaticOnSubInterfaceTest.java
index 3d88fe8..4bb1e9c 100644
--- a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceWithDefaultMethodNotInitializedByInvokeStaticOnSubInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceWithDefaultMethodNotInitializedByInvokeStaticOnSubInterfaceTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import org.junit.Test;
@@ -67,9 +68,12 @@
@Test
public void testClassInitializationMayHaveSideEffects() throws Exception {
AppView<AppInfoWithLiveness> appView =
- computeAppViewWithLiveness(buildInnerClasses(getClass()).build(), TestClass.class);
- // TODO(b/172049649): The initialization of J does not trigger the <clinit> of I.
- assertMayHaveClassInitializationSideEffects(appView, J.class);
+ computeAppViewWithLiveness(
+ buildInnerClasses(getClass())
+ .addLibraryFile(ToolHelper.getMostRecentAndroidJar())
+ .build(),
+ TestClass.class);
+ assertNoClassInitializationSideEffects(appView, J.class);
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/shaking/testrules/A.java b/src/test/java/com/android/tools/r8/shaking/testrules/A.java
index 4536cd9..1387558 100644
--- a/src/test/java/com/android/tools/r8/shaking/testrules/A.java
+++ b/src/test/java/com/android/tools/r8/shaking/testrules/A.java
@@ -4,8 +4,10 @@
package com.android.tools.r8.shaking.testrules;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoStaticClassMerging;
+@NoHorizontalClassMerging
@NoStaticClassMerging
public class A {
diff --git a/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java b/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java
index 41533ec..d4e5972 100644
--- a/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java
@@ -42,6 +42,7 @@
return testForR8(parameters.getBackend())
.addProgramClasses(Main.class, A.class, B.class, C.class)
.addKeepRules(proguardConfiguration)
+ .enableNoHorizontalClassMergingAnnotations()
.enableNoStaticClassMergingAnnotations()
.enableProguardTestOptions()
.compile()
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesArrayTypesTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesArrayTypesTest.java
index 0074a89..a1b4180 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesArrayTypesTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesArrayTypesTest.java
@@ -8,6 +8,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.DiagnosticsChecker;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -41,13 +42,13 @@
boolean acceptMethodCalled;
@Override
- public void acceptType(TracedClass tracedClass) {
+ public void acceptType(TracedClass tracedClass, DiagnosticsHandler handler) {
assertFalse(tracedClass.isMissingDefinition());
tracedTypes.add(tracedClass.getReference());
}
@Override
- public void acceptField(TracedField tracedField) {
+ public void acceptField(TracedField tracedField, DiagnosticsHandler handler) {
acceptFieldCalled = true;
assertFalse(tracedField.isMissingDefinition());
assertEquals(
@@ -57,7 +58,7 @@
}
@Override
- public void acceptMethod(TracedMethod tracedMethod) {
+ public void acceptMethod(TracedMethod tracedMethod, DiagnosticsHandler handler) {
acceptMethodCalled = true;
assertFalse(tracedMethod.isMissingDefinition());
assertEquals(
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
index 77c1e53..d354df7 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
@@ -9,24 +9,27 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.DiagnosticsChecker;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.tracereferences.TraceReferencesFormattingConsumer.OutputFormat;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.ZipUtils;
import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
import com.google.common.collect.ImmutableList;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.PrintStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -62,10 +65,21 @@
@Test(expected = CompilationFailedException.class)
public void emptyRunCommandLine() throws Throwable {
DiagnosticsChecker.checkErrorsContains(
- "No library specified",
+ "Missing command",
handler -> {
TraceReferences.run(
- TraceReferencesCommand.parse(new String[] {""}, Origin.unknown(), handler).build());
+ TraceReferencesCommand.parse(new String[] {}, Origin.unknown(), handler).build());
+ });
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void unsupportedCommandCommandLine() throws Throwable {
+ DiagnosticsChecker.checkErrorsContains(
+ "Missing command, specify one of 'check', '--print-usage' or '--keep-rules'",
+ handler -> {
+ TraceReferences.run(
+ TraceReferencesCommand.parse(new String[] {"--xxx"}, Origin.unknown(), handler)
+ .build());
});
}
@@ -89,7 +103,7 @@
TraceReferences.run(
TraceReferencesCommand.parse(
new String[] {
- "--lib", ToolHelper.getAndroidJar(AndroidApiLevel.P).toString()
+ "--check", "--lib", ToolHelper.getAndroidJar(AndroidApiLevel.P).toString()
},
Origin.unknown(),
handler)
@@ -98,24 +112,39 @@
}
@Test(expected = CompilationFailedException.class)
- public void invalidFormatCommandLine() throws Throwable {
+ public void multipleCommandsSpecified() throws Throwable {
DiagnosticsChecker.checkErrorsContains(
- "Unsupported format 'xxx'",
+ "Multiple commands specified",
handler -> {
TraceReferences.run(
TraceReferencesCommand.parse(
- new String[] {"--format", "xxx"}, Origin.unknown(), handler)
+ new String[] {"--check", "--keep-rules"}, Origin.unknown(), handler)
.build());
});
}
@Test(expected = CompilationFailedException.class)
- public void missingFormatCommandLine() throws Throwable {
+ public void allowobfuscationWithoutKeepRule() throws Throwable {
DiagnosticsChecker.checkErrorsContains(
- "Missing parameter for --format",
+ "Using '--allowobfuscation' requires command '--keep-rules'",
handler -> {
TraceReferences.run(
- TraceReferencesCommand.parse(new String[] {"--format"}, Origin.unknown(), handler)
+ TraceReferencesCommand.parse(
+ new String[] {"--check", "--allowobfuscation"}, Origin.unknown(), handler)
+ .build());
+ });
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void allowobfuscationMultiple() throws Throwable {
+ DiagnosticsChecker.checkErrorsContains(
+ "No library specified",
+ handler -> {
+ TraceReferences.run(
+ TraceReferencesCommand.parse(
+ new String[] {"--keep-rules", "--allowobfuscation", "--allowobfuscation"},
+ Origin.unknown(),
+ handler)
.build());
});
}
@@ -123,11 +152,23 @@
@Test(expected = CompilationFailedException.class)
public void multipleFormatsCommandLine() throws Throwable {
DiagnosticsChecker.checkErrorsContains(
- "--format specified multiple times",
+ "Using '--output' requires command '--print-usage' or '--keep-rules'",
handler -> {
TraceReferences.run(
TraceReferencesCommand.parse(
- new String[] {"--format", "printuses", "--format", "keep"},
+ new String[] {"--check", "--output", "xxx"}, Origin.unknown(), handler)
+ .build());
+ });
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void outputMultiple() throws Throwable {
+ DiagnosticsChecker.checkErrorsContains(
+ "Option '--output' passed multiple times",
+ handler -> {
+ TraceReferences.run(
+ TraceReferencesCommand.parse(
+ new String[] {"--keep-rules", "--output", "xxx", "--output", "xxx"},
Origin.unknown(),
handler)
.build());
@@ -136,13 +177,44 @@
private String formatName(OutputFormat format) {
if (format == OutputFormat.PRINTUSAGE) {
- return "printuses";
+ return "--print-usage";
}
if (format == OutputFormat.KEEP_RULES) {
- return "keep";
+ return "--keep-rules";
}
assertSame(format, OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION);
- return "keepallowobfuscation";
+ return "--keep-rules";
+ }
+
+ enum OutputFormat {
+ /** Format used with the -printusage flag */
+ PRINTUSAGE,
+ /** Keep rules keeping each of the traced references */
+ KEEP_RULES,
+ /**
+ * Keep rules with <code>allowobfuscation</code> modifier keeping each of the traced references
+ */
+ KEEP_RULES_WITH_ALLOWOBFUSCATION
+ }
+
+ private static class StringValueStringConsumer implements StringConsumer {
+ private StringBuilder builder = new StringBuilder();
+ private boolean finished = false;
+
+ @Override
+ public void accept(String string, DiagnosticsHandler handler) {
+ builder.append(string);
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ finished = true;
+ }
+
+ String get() {
+ assert finished;
+ return builder.toString();
+ }
}
public void runAndCheckOutput(
@@ -155,7 +227,14 @@
Path dir = temp.newFolder().toPath();
Path output = dir.resolve("output.txt");
DiagnosticsChecker diagnosticsChecker = new DiagnosticsChecker();
- TraceReferencesFormattingConsumer consumer = new TraceReferencesFormattingConsumer(format);
+ StringValueStringConsumer stringConsumer = new StringValueStringConsumer();
+ TraceReferencesConsumer consumer =
+ format == OutputFormat.PRINTUSAGE
+ ? new TraceReferencesPrintUsage()
+ : TraceReferencesKeepRules.builder()
+ .setAllowObfuscation(format == OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION)
+ .setOutputConsumer(stringConsumer)
+ .build();
try {
TraceReferences.run(
TraceReferencesCommand.builder(diagnosticsChecker)
@@ -164,7 +243,11 @@
.addSourceFiles(sourceJar)
.setConsumer(consumer)
.build());
- assertEquals(expected, consumer.get());
+ if (consumer instanceof TraceReferencesPrintUsage) {
+ assertEquals(expected, ((TraceReferencesPrintUsage) consumer).get());
+ } else {
+ assertEquals(expected, stringConsumer.get());
+ }
if (diagnosticsCheckerConsumer != null) {
diagnosticsCheckerConsumer.accept(diagnosticsChecker);
} else {
@@ -179,17 +262,23 @@
throw e;
}
- TraceReferences.run(
- TraceReferencesCommand.parse(
- new String[] {
- "--lib", ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(),
- "--target", targetJar.toString(),
- "--source", sourceJar.toString(),
- "--output", output.toString(),
- "--format", formatName(format)
- },
- Origin.unknown())
- .build());
+ List<String> args = new ArrayList<>();
+ args.add(formatName(format));
+ if (format == OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION) {
+ args.add("--allowobfuscation");
+ }
+ args.addAll(
+ ImmutableList.of(
+ "--lib",
+ ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(),
+ "--target",
+ targetJar.toString(),
+ "--source",
+ sourceJar.toString(),
+ "--output",
+ output.toString()));
+
+ TraceReferences.run(TraceReferencesCommand.parse(args, Origin.unknown()).build());
assertEquals(expected, FileUtils.readTextFile(output, Charsets.UTF_8));
}
@@ -202,6 +291,16 @@
runAndCheckOutput(targetClasses, sourceClasses, format, expected, null);
}
+ private Path zipWithTestClasses(Path zipFile, List<Class<?>> targetClasses) throws IOException {
+ return ZipBuilder.builder(zipFile)
+ .addFilesRelative(
+ ToolHelper.getClassPathForTests(),
+ targetClasses.stream()
+ .map(ToolHelper::getClassFileForTestClass)
+ .collect(Collectors.toList()))
+ .build();
+ }
+
public void runAndCheckOutput(
List<Class<?>> targetClasses,
List<Class<?>> sourceClasses,
@@ -210,22 +309,8 @@
Consumer<DiagnosticsChecker> diagnosticsCheckerConsumer)
throws Throwable {
Path dir = temp.newFolder().toPath();
- Path targetJar =
- ZipBuilder.builder(dir.resolve("target.jar"))
- .addFilesRelative(
- ToolHelper.getClassPathForTests(),
- targetClasses.stream()
- .map(ToolHelper::getClassFileForTestClass)
- .collect(Collectors.toList()))
- .build();
- Path sourceJar =
- ZipBuilder.builder(dir.resolve("source.jar"))
- .addFilesRelative(
- ToolHelper.getClassPathForTests(),
- sourceClasses.stream()
- .map(ToolHelper::getClassFileForTestClass)
- .collect(Collectors.toList()))
- .build();
+ Path targetJar = zipWithTestClasses(dir.resolve("target.jar"), targetClasses);
+ Path sourceJar = zipWithTestClasses(dir.resolve("source.jar"), sourceClasses);
runAndCheckOutput(targetJar, sourceJar, format, expected, diagnosticsCheckerConsumer);
}
@@ -276,6 +361,118 @@
"-keeppackagenames com.android.tools.r8.tracereferences")));
}
+ @Test
+ public void test_noOutput() throws Throwable {
+ Path dir = temp.newFolder().toPath();
+ Path targetJar = zipWithTestClasses(dir.resolve("target.jar"), ImmutableList.of(Target.class));
+ Path sourceJar = zipWithTestClasses(dir.resolve("source.jar"), ImmutableList.of(Source.class));
+ PrintStream originalOut = System.out;
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ TraceReferences.run(
+ TraceReferencesCommand.builder()
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .addTargetFiles(targetJar)
+ .addSourceFiles(sourceJar)
+ .setConsumer(TraceReferencesConsumer.emptyConsumer())
+ .build());
+ assertEquals(0, baos.size());
+ } finally {
+ System.setOut(originalOut);
+ }
+
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ TraceReferences.run(
+ TraceReferencesCommand.parse(
+ new String[] {
+ "--lib",
+ ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(),
+ "--check",
+ "--target",
+ targetJar.toString(),
+ "--source",
+ sourceJar.toString(),
+ },
+ Origin.unknown())
+ .build());
+ assertEquals(0, baos.size());
+ } finally {
+ System.setOut(originalOut);
+ }
+ }
+
+ @Test
+ public void test_stdoutOutput() throws Throwable {
+ String expected =
+ StringUtils.lines(
+ ImmutableList.of(
+ "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target",
+ "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target: void"
+ + " method(int)",
+ "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target: int"
+ + " field"));
+ Path dir = temp.newFolder().toPath();
+ Path targetJar = zipWithTestClasses(dir.resolve("target.jar"), ImmutableList.of(Target.class));
+ Path sourceJar = zipWithTestClasses(dir.resolve("source.jar"), ImmutableList.of(Source.class));
+ PrintStream originalOut = System.out;
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ TraceReferences.run(
+ TraceReferencesCommand.parse(
+ new String[] {
+ "--lib",
+ ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(),
+ "--target",
+ targetJar.toString(),
+ "--source",
+ sourceJar.toString(),
+ "--print-usage",
+ },
+ Origin.unknown())
+ .build());
+ assertEquals(expected, baos.toString(Charsets.UTF_8.name()));
+ } finally {
+ System.setOut(originalOut);
+ }
+ }
+
+ public void classFileInput() throws Throwable {
+ String expected =
+ StringUtils.lines(
+ ImmutableList.of(
+ "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target",
+ "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target: void"
+ + " method(int)",
+ "com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target: int"
+ + " field"));
+ TraceReferencesPrintUsage consumer = new TraceReferencesPrintUsage();
+ TraceReferences.run(
+ TraceReferencesCommand.builder()
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .addTargetFiles(ToolHelper.getClassFileForTestClass(Target.class))
+ .addSourceFiles(ToolHelper.getClassFileForTestClass(Source.class))
+ .setConsumer(consumer)
+ .build());
+ assertEquals(expected, consumer.get());
+
+ Path output = temp.newFile().toPath();
+ TraceReferences.run(
+ TraceReferencesCommand.parse(
+ new String[] {
+ "--lib", ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(),
+ "--target", ToolHelper.getClassFileForTestClass(Target.class).toString(),
+ "--source", ToolHelper.getClassFileForTestClass(Source.class).toString(),
+ "--output", output.toString(),
+ },
+ Origin.unknown())
+ .build());
+ assertEquals(expected, FileUtils.readTextFile(output, Charsets.UTF_8));
+ }
+
private void checkTargetMissing(DiagnosticsChecker diagnosticsChecker) {
Field field;
Method method;
@@ -344,32 +541,19 @@
public void testMissingReference_errorToWarning() throws Throwable {
Path dir = temp.newFolder().toPath();
Path targetJar =
- ZipBuilder.builder(dir.resolve("target.jar"))
- .addFilesRelative(
- ToolHelper.getClassPathForTests(),
- ToolHelper.getClassFileForTestClass(OtherTarget.class))
- .build();
- Path sourceJar =
- ZipBuilder.builder(dir.resolve("source.jar"))
- .addFilesRelative(
- ToolHelper.getClassPathForTests(),
- ToolHelper.getClassFileForTestClass(Source.class))
- .build();
- Path output = dir.resolve("output.txt");
+ zipWithTestClasses(dir.resolve("target.jar"), ImmutableList.of(OtherTarget.class));
+ Path sourceJar = zipWithTestClasses(dir.resolve("source.jar"), ImmutableList.of(Source.class));
DiagnosticsChecker diagnosticsChecker = new DiagnosticsChecker();
TraceReferences.run(
TraceReferencesCommand.parse(
new String[] {
+ "--check",
"--lib",
ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(),
"--target",
targetJar.toString(),
"--source",
sourceJar.toString(),
- "--output",
- output.toString(),
- "--format",
- formatName(OutputFormat.PRINTUSAGE),
"--map-diagnostics:MissingDefinitionsDiagnostic",
"error",
"warning"
@@ -407,12 +591,7 @@
.addBytes(
DescriptorUtils.getPathFromJavaType(Target.class), getClassWithTargetRemoved())
.build();
- Path sourceJar =
- ZipBuilder.builder(dir.resolve("source.jar"))
- .addFilesRelative(
- ToolHelper.getClassPathForTests(),
- ToolHelper.getClassFileForTestClass(Source.class))
- .build();
+ Path sourceJar = zipWithTestClasses(dir.resolve("source.jar"), ImmutableList.of(Source.class));
try {
runAndCheckOutput(
targetJar,
@@ -436,16 +615,7 @@
.addBytes(
DescriptorUtils.getPathFromJavaType(Target.class), getClassWithTargetRemoved())
.build();
- Path sourceJar =
- ZipBuilder.builder(dir.resolve("source.jar"))
- .addFilesRelative(
- ToolHelper.getClassPathForTests(),
- ToolHelper.getClassFileForTestClass(Source.class))
- .build();
- ZipUtils.zip(
- sourceJar,
- ToolHelper.getClassPathForTests(),
- ToolHelper.getClassFileForTestClass(Source.class));
+ Path sourceJar = zipWithTestClasses(dir.resolve("source.jar"), ImmutableList.of(Source.class));
try {
runAndCheckOutput(
targetJar,
@@ -473,12 +643,7 @@
.addBytes(
DescriptorUtils.getPathFromJavaType(Target.class), getClassWithTargetRemoved())
.build();
- Path sourceJar =
- ZipBuilder.builder(dir.resolve("source.jar"))
- .addFilesRelative(
- ToolHelper.getClassPathForTests(),
- ToolHelper.getClassFileForTestClass(Source.class))
- .build();
+ Path sourceJar = zipWithTestClasses(dir.resolve("source.jar"), ImmutableList.of(Source.class));
try {
runAndCheckOutput(
targetJar,
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java
index b21d409..551c8a1 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java
@@ -7,12 +7,14 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.DiagnosticsChecker;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
import com.google.common.collect.ImmutableList;
@@ -193,6 +195,80 @@
}
}
+ static class FailingConsumer implements TraceReferencesConsumer {
+ private final String where;
+
+ FailingConsumer(String where) {
+ this.where = where;
+ }
+
+ @Override
+ public void acceptType(TracedClass tracedClass, DiagnosticsHandler handler) {
+ if (where.equals("acceptType")) {
+ handler.error(new StringDiagnostic("Error in " + where));
+ }
+ }
+
+ @Override
+ public void acceptField(TracedField tracedField, DiagnosticsHandler handler) {
+ if (where.equals("acceptField")) {
+ handler.error(new StringDiagnostic("Error in " + where));
+ }
+ }
+
+ @Override
+ public void acceptMethod(TracedMethod tracedMethod, DiagnosticsHandler handler) {
+ if (where.equals("acceptMethod")) {
+ handler.error(new StringDiagnostic("Error in " + where));
+ }
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ if (where.equals("finished")) {
+ handler.error(new StringDiagnostic("Error in " + where));
+ }
+ }
+ }
+
+ @Test
+ public void traceReferencesConsumerError() throws Throwable {
+ Path dir = temp.newFolder().toPath();
+ Path targetJar =
+ ZipBuilder.builder(dir.resolve("target.jar"))
+ .addFilesRelative(
+ ToolHelper.getClassPathForTests(),
+ ToolHelper.getClassFileForTestClass(Target.class),
+ ToolHelper.getClassFileForTestClass(Target1.class),
+ ToolHelper.getClassFileForTestClass(Target2.class),
+ ToolHelper.getClassFileForTestClass(Target3.class))
+ .build();
+ Path sourceJar =
+ ZipBuilder.builder(dir.resolve("source.jar"))
+ .addFilesRelative(
+ ToolHelper.getClassPathForTests(),
+ ToolHelper.getClassFileForTestClass(Source.class))
+ .build();
+
+ for (String where : new String[] {"acceptType", "acceptField", "acceptMethod", "finished"}) {
+ try {
+ DiagnosticsChecker.checkErrorsContains(
+ "Error in " + where,
+ handler ->
+ TraceReferences.run(
+ TraceReferencesCommand.builder(handler)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .addSourceFiles(sourceJar)
+ .addTargetFiles(targetJar)
+ .setConsumer(new FailingConsumer(where))
+ .build()));
+ fail("Unexpected success");
+ } catch (CompilationFailedException e) {
+ // Expected.
+ }
+ }
+ }
+
static class Target1 {}
static class Target2 {}
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesMissingReferencesInDexTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesMissingReferencesInDexTest.java
index 1387c3f..b5b26b0 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesMissingReferencesInDexTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesMissingReferencesInDexTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.DiagnosticsChecker;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -39,14 +40,14 @@
boolean acceptMethodCalled;
@Override
- public void acceptType(TracedClass tracedClass) {
+ public void acceptType(TracedClass tracedClass, DiagnosticsHandler handler) {
acceptTypeCalled = true;
assertEquals(Reference.classFromClass(Target.class), tracedClass.getReference());
assertTrue(tracedClass.isMissingDefinition());
}
@Override
- public void acceptField(TracedField tracedField) {
+ public void acceptField(TracedField tracedField, DiagnosticsHandler handler) {
acceptFieldCalled = true;
assertEquals(
Reference.classFromClass(Target.class), tracedField.getReference().getHolderClass());
@@ -55,7 +56,7 @@
}
@Override
- public void acceptMethod(TracedMethod tracedMethod) {
+ public void acceptMethod(TracedMethod tracedMethod, DiagnosticsHandler handler) {
acceptMethodCalled = true;
assertEquals(
Reference.classFromClass(Target.class), tracedMethod.getReference().getHolderClass());
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
index a440523..0d99817 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
@@ -10,7 +10,10 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
+import java.util.Set;
+import java.util.function.BiConsumer;
public class HorizontallyMergedClassesInspector {
@@ -23,6 +26,14 @@
this.horizontallyMergedClasses = horizontallyMergedClasses;
}
+ public void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer) {
+ horizontallyMergedClasses.forEachMergeGroup(consumer);
+ }
+
+ public DexType getTarget(DexType clazz) {
+ return horizontallyMergedClasses.getMergeTargetOrDefault(clazz);
+ }
+
public HorizontallyMergedClassesInspector assertMergedInto(Class<?> from, Class<?> target) {
assertEquals(
horizontallyMergedClasses.getMergeTargetOrDefault(toDexType(from, dexItemFactory)),
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedLambdaClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedLambdaClassesInspector.java
index 4b3579f..421b54a 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedLambdaClassesInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedLambdaClassesInspector.java
@@ -5,10 +5,14 @@
package com.android.tools.r8.utils.codeinspector;
import static com.android.tools.r8.TestBase.toDexType;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
+import java.util.Set;
+import java.util.function.BiConsumer;
public class HorizontallyMergedLambdaClassesInspector {
@@ -33,4 +37,13 @@
}
return this;
}
+
+ public HorizontallyMergedLambdaClassesInspector assertClassNotMerged(Class<?> clazz) {
+ assertFalse(horizontallyMergedLambdaClasses.hasBeenMerged(toDexType(clazz, dexItemFactory)));
+ return this;
+ }
+
+ public void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer) {
+ horizontallyMergedLambdaClasses.forEachMergeGroup(consumer);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/StaticallyMergedClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/StaticallyMergedClassesInspector.java
new file mode 100644
index 0000000..0b45f27
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/StaticallyMergedClassesInspector.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.codeinspector;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.classmerging.StaticallyMergedClasses;
+import java.util.Set;
+import java.util.function.BiConsumer;
+
+public class StaticallyMergedClassesInspector {
+
+ private final DexItemFactory dexItemFactory;
+ private final StaticallyMergedClasses staticallyMergedClasses;
+
+ public StaticallyMergedClassesInspector(
+ DexItemFactory dexItemFactory, StaticallyMergedClasses staticallyMergedClasses) {
+ this.dexItemFactory = dexItemFactory;
+ this.staticallyMergedClasses = staticallyMergedClasses;
+ }
+
+ public void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer) {
+ staticallyMergedClasses.forEachMergeGroup(consumer);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/VerticallyMergedClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/VerticallyMergedClassesInspector.java
index bd64711..05031f2 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/VerticallyMergedClassesInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/VerticallyMergedClassesInspector.java
@@ -32,4 +32,9 @@
}
return this;
}
+
+ public VerticallyMergedClassesInspector assertNoClassesMerged() {
+ assertTrue(verticallyMergedClasses.isEmpty());
+ return this;
+ }
}
diff --git a/src/test/java/com/android/tools/r8/utils/structural/StructuralItemsCustomOrderTest.java b/src/test/java/com/android/tools/r8/utils/structural/StructuralItemsCustomOrderTest.java
new file mode 100644
index 0000000..cfea69c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/structural/StructuralItemsCustomOrderTest.java
@@ -0,0 +1,170 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils.structural;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+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.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class StructuralItemsCustomOrderTest extends TestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public StructuralItemsCustomOrderTest(TestParameters parameters) {
+ parameters.assertNoneRuntime();
+ }
+
+ final B b1 = new B(1);
+ final B b2 = new B(2);
+ final B b2_copy = new B(2);
+
+ final A a1b1 = new A(1, b1);
+ final A a2b1 = new A(2, b1);
+ final A a1b2 = new A(1, b2);
+ final A a2b2 = new A(2, b2);
+ final A a1b2_copy = new A(1, b2_copy);
+ final A a2b2_copy = new A(2, b2_copy);
+
+ @Test
+ public void testOrder() {
+ assertFalse(b1.isLessThan(b2));
+ assertTrue(b2.isEqualTo(b2_copy));
+ assertTrue(b2.isLessThan(b1));
+
+ assertFalse(a1b1.isLessThan(a1b2));
+ assertTrue(a1b2.isLessThan(a2b1));
+ assertFalse(a2b1.isLessThan(a1b2_copy));
+ }
+
+ @Test
+ public void testOrderWithIdentityEquivalence() {
+ // These mirror the above exactly but using the compare result.
+ RepresentativeMap map = t -> t;
+
+ assertFalse(b1.compareWithTypeEquivalenceTo(b2, map) < 0);
+ assertTrue(b2.compareWithTypeEquivalenceTo(b2_copy, map) == 0);
+ assertTrue(b2.compareWithTypeEquivalenceTo(b1, map) < 0);
+
+ assertFalse(a1b1.compareWithTypeEquivalenceTo(a1b2, map) < 0);
+ assertTrue(a1b2.compareWithTypeEquivalenceTo(a2b1, map) < 0);
+ assertFalse(a2b1.compareWithTypeEquivalenceTo(a1b2_copy, map) < 0);
+ }
+
+ @Test
+ public void testEquals() {
+ assertFalse(b1.isEqualTo(b2));
+ assertTrue(b2.isEqualTo(b2_copy));
+ assertEquals(b2, b2_copy);
+
+ assertFalse(a2b2.isEqualTo(a2b1));
+ assertTrue(a1b2.isEqualTo(a1b2_copy));
+ assertEquals(a2b2, a2b2_copy);
+
+ // Type incompatible check should still work.
+ assertNotEquals(b1, a1b1);
+ }
+
+ @Test
+ public void testHashCode() {
+ Set<B> bs = new HashSet<>(ImmutableList.of(b1, b2, b2_copy));
+ assertEquals(ImmutableSet.of(b1, b2), bs);
+
+ Set<A> as = new HashSet<>(ImmutableList.of(a1b1, a1b2, a2b1, a2b2, a1b2_copy, a2b2_copy));
+ assertEquals(ImmutableSet.of(a1b1, a1b2, a2b1, a2b2), as);
+
+ // If these collide it is a poor hashing algorithm...
+ assertNotEquals(b1.hashCode(), b2.hashCode());
+ }
+
+ private static class A implements StructuralItem<A> {
+
+ private final int x;
+ private final B b;
+
+ private static void accept(StructuralSpecification<A, ?> spec) {
+ spec.withInt(a -> a.x).withItem(a -> a.b);
+ }
+
+ public A(int x, B b) {
+ this.x = x;
+ this.b = b;
+ }
+
+ @Override
+ public StructuralAccept<A> getStructuralAccept() {
+ return A::accept;
+ }
+
+ @Override
+ public A self() {
+ return this;
+ }
+
+ @Override
+ public final boolean equals(Object other) {
+ return Equatable.equalsImpl(this, other);
+ }
+
+ @Override
+ public final int hashCode() {
+ return HashCodeVisitor.run(this, A::accept);
+ }
+ }
+
+ private static class B implements StructuralItem<B> {
+
+ private final int y;
+
+ private static void accept(StructuralSpecification<B, ?> spec) {
+ spec.withInt(b -> b.y);
+ }
+
+ public B(int y) {
+ this.y = y;
+ }
+
+ @Override
+ public StructuralAccept<B> getStructuralAccept() {
+ return B::accept;
+ }
+
+ @Override
+ public B self() {
+ return this;
+ }
+
+ @Override
+ public final boolean equals(Object other) {
+ return Equatable.equalsImpl(this, other);
+ }
+
+ @Override
+ public final int hashCode() {
+ return HashCodeVisitor.run(this, B::accept);
+ }
+
+ // Override allowing a change to the order of any type of compare-to visitation, e.g., with
+ // and without a type equivalence map.
+ @Override
+ public void acceptCompareTo(B other, CompareToVisitor visitor) {
+ visitor.visit(other, this, B::accept);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/structural/StructuralItemsTest.java b/src/test/java/com/android/tools/r8/utils/structural/StructuralItemsTest.java
new file mode 100644
index 0000000..64481c3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/structural/StructuralItemsTest.java
@@ -0,0 +1,182 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils.structural;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+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.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class StructuralItemsTest extends TestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public StructuralItemsTest(TestParameters parameters) {
+ parameters.assertNoneRuntime();
+ }
+
+ final B b1 = new B(1);
+ final B b2 = new B(2);
+ final B b2_copy = new B(2);
+
+ final A a1b1 = new A(1, b1);
+ final A a2b1 = new A(2, b1);
+ final A a1b2 = new A(1, b2);
+ final A a2b2 = new A(2, b2);
+ final A a1b2_copy = new A(1, b2_copy);
+ final A a2b2_copy = new A(2, b2_copy);
+
+ @Test
+ public void testOrder() {
+ assertTrue(b1.isLessThan(b2));
+ assertTrue(b2.isEqualTo(b2_copy));
+ assertFalse(b2.isLessThan(b1));
+
+ assertTrue(a1b1.isLessThan(a1b2));
+ assertTrue(a1b2.isLessThan(a2b1));
+ assertFalse(a2b1.isLessThan(a1b2_copy));
+
+ assertEquals(b1, Ordered.minIgnoreNull(null, b1));
+ assertEquals(b1, Ordered.minIgnoreNull(b1, null));
+ assertEquals(b1, Ordered.maxIgnoreNull(null, b1));
+ assertEquals(b1, Ordered.maxIgnoreNull(b1, null));
+ }
+
+ @Test
+ public void testEquals() {
+ assertFalse(b1.isEqualTo(b2));
+ assertTrue(b2.isEqualTo(b2_copy));
+ assertEquals(b2, b2_copy);
+
+ assertFalse(a2b2.isEqualTo(a2b1));
+ assertTrue(a1b2.isEqualTo(a1b2_copy));
+ assertEquals(a2b2, a2b2_copy);
+
+ // Type incompatible check should still work.
+ assertNotEquals(b1, a1b1);
+ }
+
+ @Test
+ public void testHashCode() {
+ assertEquals(b2.hashCode(), b2_copy.hashCode());
+ assertEquals(b2, b2_copy);
+ Set<B> bs = new HashSet<>(ImmutableList.of(b1, b2, b2_copy));
+ assertEquals(ImmutableSet.of(b1, b2), bs);
+
+ Set<A> as = new HashSet<>(ImmutableList.of(a1b1, a1b2, a2b1, a2b2, a1b2_copy, a2b2_copy));
+ assertEquals(ImmutableSet.of(a1b1, a1b2, a2b1, a2b2), as);
+
+ // If these collide it is a poor hashing algorithm...
+ assertNotEquals(b1.hashCode(), b2.hashCode());
+ }
+
+ private String getHash(StructuralItem<?> item) {
+ return item.hashForTesting();
+ }
+
+ @Test
+ public void testHashing() {
+ assertEquals(getHash(b2), getHash(b2_copy));
+ assertEquals(getHash(b2), getHash(b2_copy));
+ Set<String> bs = new HashSet<>(ImmutableList.of(getHash(b1), getHash(b2), getHash(b2_copy)));
+ assertEquals(ImmutableSet.of(getHash(b1), getHash(b2)), bs);
+
+ Set<String> as =
+ ImmutableList.of(a1b1, a1b2, a2b1, a2b2, a1b2_copy, a2b2_copy).stream()
+ .map(this::getHash)
+ .collect(Collectors.toSet());
+ assertEquals(
+ ImmutableSet.of(a1b1, a1b2, a2b1, a2b2).stream()
+ .map(this::getHash)
+ .collect(Collectors.toSet()),
+ as);
+
+ // If these collide it is a poor hashing algorithm...
+ assertNotEquals(getHash(b1), getHash(b2));
+ }
+
+ private static class A implements StructuralItem<A> {
+
+ private final int x;
+ private final B b;
+
+ private static void accept(StructuralSpecification<A, ?> spec) {
+ spec.withInt(a -> a.x).withItem(a -> a.b);
+ }
+
+ public A(int x, B b) {
+ this.x = x;
+ this.b = b;
+ }
+
+ @Override
+ public StructuralAccept<A> getStructuralAccept() {
+ return A::accept;
+ }
+
+ @Override
+ public A self() {
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return Equatable.equalsImpl(this, other);
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeVisitor.run(this, A::accept);
+ }
+ }
+
+ private static class B implements StructuralItem<B> {
+
+ private final int y;
+
+ private static void accept(StructuralSpecification<B, ?> spec) {
+ spec.withInt(b -> b.y);
+ }
+
+ public B(int y) {
+ this.y = y;
+ }
+
+ @Override
+ public StructuralAccept<B> getStructuralAccept() {
+ return B::accept;
+ }
+
+ @Override
+ public B self() {
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return Equatable.equalsImpl(this, other);
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeVisitor.run(this, B::accept);
+ }
+ }
+}
diff --git a/src/test/javaStubs/Supplier.java b/src/test/javaStubs/Supplier.java
new file mode 100644
index 0000000..f9c4860
--- /dev/null
+++ b/src/test/javaStubs/Supplier.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package java.util.function;
+
+public interface Supplier {
+ Object get();
+}
diff --git a/third_party/opensource-apps/android/compose-samples/crane.tar.gz.sha1 b/third_party/opensource-apps/android/compose-samples/crane.tar.gz.sha1
new file mode 100644
index 0000000..43b6ec0
--- /dev/null
+++ b/third_party/opensource-apps/android/compose-samples/crane.tar.gz.sha1
@@ -0,0 +1 @@
+54e1cfb2bd83e005ccd07179958261d5ed2c7102
\ No newline at end of file
diff --git a/third_party/opensource-apps/android/compose-samples/jetcaster.tar.gz.sha1 b/third_party/opensource-apps/android/compose-samples/jetcaster.tar.gz.sha1
new file mode 100644
index 0000000..e4fdd09
--- /dev/null
+++ b/third_party/opensource-apps/android/compose-samples/jetcaster.tar.gz.sha1
@@ -0,0 +1 @@
+158d1e78d2055793960120b1c58654f83cd6d4d3
\ No newline at end of file
diff --git a/third_party/opensource-apps/android/compose-samples/jetchat.tar.gz.sha1 b/third_party/opensource-apps/android/compose-samples/jetchat.tar.gz.sha1
new file mode 100644
index 0000000..5e613de
--- /dev/null
+++ b/third_party/opensource-apps/android/compose-samples/jetchat.tar.gz.sha1
@@ -0,0 +1 @@
+0a6e35687efada2890624783e9936047ed10b434
\ No newline at end of file
diff --git a/third_party/opensource-apps/android/compose-samples/jetnews.tar.gz.sha1 b/third_party/opensource-apps/android/compose-samples/jetnews.tar.gz.sha1
new file mode 100644
index 0000000..3fb24df
--- /dev/null
+++ b/third_party/opensource-apps/android/compose-samples/jetnews.tar.gz.sha1
@@ -0,0 +1 @@
+d1c89d1a22c716d3c9e2c8b7b725bc4716d12ea6
\ No newline at end of file
diff --git a/third_party/opensource-apps/android/compose-samples/jetsnack.tar.gz.sha1 b/third_party/opensource-apps/android/compose-samples/jetsnack.tar.gz.sha1
new file mode 100644
index 0000000..9562d67
--- /dev/null
+++ b/third_party/opensource-apps/android/compose-samples/jetsnack.tar.gz.sha1
@@ -0,0 +1 @@
+2e7d404796f7c4b20f47957fef00755665623526
\ No newline at end of file
diff --git a/third_party/opensource-apps/android/compose-samples/jetsurvey.tar.gz.sha1 b/third_party/opensource-apps/android/compose-samples/jetsurvey.tar.gz.sha1
new file mode 100644
index 0000000..736edd4
--- /dev/null
+++ b/third_party/opensource-apps/android/compose-samples/jetsurvey.tar.gz.sha1
@@ -0,0 +1 @@
+0dc41fbe14dbfb3bfc70ed64ff129b311bfcbf94
\ No newline at end of file
diff --git a/third_party/opensource-apps/android/compose-samples/owl.tar.gz.sha1 b/third_party/opensource-apps/android/compose-samples/owl.tar.gz.sha1
new file mode 100644
index 0000000..d6c218c
--- /dev/null
+++ b/third_party/opensource-apps/android/compose-samples/owl.tar.gz.sha1
@@ -0,0 +1 @@
+9bb9c1cc3fea6d4ceb33df8e99057caa1bbe94f6
\ No newline at end of file
diff --git a/third_party/opensource-apps/android/compose-samples/rally.tar.gz.sha1 b/third_party/opensource-apps/android/compose-samples/rally.tar.gz.sha1
new file mode 100644
index 0000000..3947935
--- /dev/null
+++ b/third_party/opensource-apps/android/compose-samples/rally.tar.gz.sha1
@@ -0,0 +1 @@
+86f3577a407a3cc3883f2d016d23879f3e8bd64e
\ No newline at end of file
diff --git a/third_party/opensource-apps/kiss.tar.gz.sha1 b/third_party/opensource-apps/kiss.tar.gz.sha1
new file mode 100644
index 0000000..6d9e214
--- /dev/null
+++ b/third_party/opensource-apps/kiss.tar.gz.sha1
@@ -0,0 +1 @@
+ae4ff6d906f2840ee6a855e989e8b563f948ba73
\ No newline at end of file
diff --git a/third_party/opensource-apps/materialistic.tar.gz.sha1 b/third_party/opensource-apps/materialistic.tar.gz.sha1
new file mode 100644
index 0000000..174a3fb
--- /dev/null
+++ b/third_party/opensource-apps/materialistic.tar.gz.sha1
@@ -0,0 +1 @@
+e392c3c4a607bbfe3ce0d2827475f388ab8b33d9
\ No newline at end of file
diff --git a/third_party/opensource-apps/minimal-todo.tar.gz.sha1 b/third_party/opensource-apps/minimal-todo.tar.gz.sha1
new file mode 100644
index 0000000..1bbe241
--- /dev/null
+++ b/third_party/opensource-apps/minimal-todo.tar.gz.sha1
@@ -0,0 +1 @@
+70f5f95146d09c6777c88e4d556ec7419ffbe35b
\ No newline at end of file
diff --git a/third_party/opensource-apps/muzei.tar.gz.sha1 b/third_party/opensource-apps/muzei.tar.gz.sha1
new file mode 100644
index 0000000..d8739a4
--- /dev/null
+++ b/third_party/opensource-apps/muzei.tar.gz.sha1
@@ -0,0 +1 @@
+39e660b93c0efc403d5d6ab7a3e947f18c0c6bb0
\ No newline at end of file
diff --git a/third_party/opensource-apps/newpipe.tar.gz.sha1 b/third_party/opensource-apps/newpipe.tar.gz.sha1
new file mode 100644
index 0000000..ee2d9cf
--- /dev/null
+++ b/third_party/opensource-apps/newpipe.tar.gz.sha1
@@ -0,0 +1 @@
+9f824b7c6e0e923401def9006d98d2f70ef175c3
\ No newline at end of file
diff --git a/third_party/opensource-apps/rover-android.tar.gz.sha1 b/third_party/opensource-apps/rover-android.tar.gz.sha1
new file mode 100644
index 0000000..043d3e4
--- /dev/null
+++ b/third_party/opensource-apps/rover-android.tar.gz.sha1
@@ -0,0 +1 @@
+50562618faf112ec73219488fa9b3705dc42a38e
\ No newline at end of file
diff --git a/third_party/opensource-apps/santa-tracker.tar.gz.sha1 b/third_party/opensource-apps/santa-tracker.tar.gz.sha1
new file mode 100644
index 0000000..ea1af87
--- /dev/null
+++ b/third_party/opensource-apps/santa-tracker.tar.gz.sha1
@@ -0,0 +1 @@
+5348b1c66c86c09c7d3bafd65b0d5417df78bcc8
\ No newline at end of file
diff --git a/third_party/opensource-apps/signal-android.tar.gz.sha1 b/third_party/opensource-apps/signal-android.tar.gz.sha1
new file mode 100644
index 0000000..131500f
--- /dev/null
+++ b/third_party/opensource-apps/signal-android.tar.gz.sha1
@@ -0,0 +1 @@
+2dbd3f913897b4ec9e25598d783579dbb74fab24
\ No newline at end of file
diff --git a/third_party/opensource-apps/simple-calendar.tar.gz.sha1 b/third_party/opensource-apps/simple-calendar.tar.gz.sha1
new file mode 100644
index 0000000..093d104
--- /dev/null
+++ b/third_party/opensource-apps/simple-calendar.tar.gz.sha1
@@ -0,0 +1 @@
+118c5a291b675393698e4a2c0a0e1891a0933a8b
\ No newline at end of file
diff --git a/third_party/opensource-apps/simple-camera.tar.gz.sha1 b/third_party/opensource-apps/simple-camera.tar.gz.sha1
new file mode 100644
index 0000000..3d88a7a
--- /dev/null
+++ b/third_party/opensource-apps/simple-camera.tar.gz.sha1
@@ -0,0 +1 @@
+19aba96f4c6b9751844ff668202a4591136e9702
\ No newline at end of file
diff --git a/third_party/opensource-apps/simple-file-manager.tar.gz.sha1 b/third_party/opensource-apps/simple-file-manager.tar.gz.sha1
new file mode 100644
index 0000000..00c4197
--- /dev/null
+++ b/third_party/opensource-apps/simple-file-manager.tar.gz.sha1
@@ -0,0 +1 @@
+5f93c93b767b755fdec101961d9599928bf0aec8
\ No newline at end of file
diff --git a/third_party/opensource-apps/simple-gallery.tar.gz.sha1 b/third_party/opensource-apps/simple-gallery.tar.gz.sha1
new file mode 100644
index 0000000..b2d42db
--- /dev/null
+++ b/third_party/opensource-apps/simple-gallery.tar.gz.sha1
@@ -0,0 +1 @@
+7d086ce14817cf834cdbf99338aeace3f198bc67
\ No newline at end of file
diff --git a/third_party/opensource-apps/sqldelight.tar.gz.sha1 b/third_party/opensource-apps/sqldelight.tar.gz.sha1
new file mode 100644
index 0000000..3a186f8
--- /dev/null
+++ b/third_party/opensource-apps/sqldelight.tar.gz.sha1
@@ -0,0 +1 @@
+31db97e3e578398a6e036390da81732717b5e0b7
\ No newline at end of file
diff --git a/third_party/opensource-apps/tachiyomi.tar.gz.sha1 b/third_party/opensource-apps/tachiyomi.tar.gz.sha1
new file mode 100644
index 0000000..a8c7a0c
--- /dev/null
+++ b/third_party/opensource-apps/tachiyomi.tar.gz.sha1
@@ -0,0 +1 @@
+a88063982149445bcc177856b3b6c265dc0d5ea7
\ No newline at end of file
diff --git a/third_party/opensource-apps/tivi.tar.gz.sha1 b/third_party/opensource-apps/tivi.tar.gz.sha1
new file mode 100644
index 0000000..6bd16e2
--- /dev/null
+++ b/third_party/opensource-apps/tivi.tar.gz.sha1
@@ -0,0 +1 @@
+2f4adb11dcc8c56f377ee9945d47e88313bc5855
\ No newline at end of file
diff --git a/third_party/opensource-apps/tusky.tar.gz.sha1 b/third_party/opensource-apps/tusky.tar.gz.sha1
new file mode 100644
index 0000000..7eecbaf
--- /dev/null
+++ b/third_party/opensource-apps/tusky.tar.gz.sha1
@@ -0,0 +1 @@
+e546ffad98f75d0db7be39bfa7147f8eaa78d0aa
\ No newline at end of file
diff --git a/tools/compiledump.py b/tools/compiledump.py
index 48c596b..5c818d6 100755
--- a/tools/compiledump.py
+++ b/tools/compiledump.py
@@ -38,6 +38,7 @@
help='Compiler version to use (default read from dump version file).'
'Valid arguments are:'
' "master" to run from your own tree,'
+ ' "source" to run from build classes directly,'
' "X.Y.Z" to run a specific version, or'
' <hash> to run that hash from master.',
default=None)
@@ -87,6 +88,12 @@
help='Set desugared-library (default set from dump)',
default=None)
parser.add_argument(
+ '--disable-desugared-lib',
+ help='Disable desugared-libary if it will be set from dump',
+ default=False,
+ action='store_true'
+ )
+ parser.add_argument(
'--loop',
help='Run the compilation in a loop',
default=False,
@@ -131,6 +138,14 @@
def desugared_library_json(self):
return self.if_exists('desugared-library.json')
+ def proguard_input_map(self):
+ if self.if_exists('proguard_input.config'):
+ print "Unimplemented: proguard_input configuration."
+
+ def main_dex_resource(self):
+ if self.if_exists('main-dex-list.txt'):
+ print "Unimplemented: main-dex-list."
+
def build_properties_file(self):
return self.if_exists('build.properties')
@@ -209,6 +224,8 @@
def download_distribution(args, version, temp):
if version == 'master':
return utils.R8_JAR if args.nolib else utils.R8LIB_JAR
+ if version == 'source':
+ return '%s:%s' % (utils.BUILD_JAVA_MAIN_DIR, utils.ALL_DEPS_JAR)
name = 'r8.jar' if args.nolib else 'r8lib.jar'
source = archive.GetUploadDestination(version, name, is_hash(version))
dest = os.path.join(temp, 'r8.jar')
@@ -219,12 +236,14 @@
wrapper_file = os.path.join(
utils.REPO_ROOT,
'src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java')
- subprocess.check_output([
+ cmd = [
jdk.GetJavacExecutable(),
wrapper_file,
'-d', temp,
'-cp', dist,
- ])
+ ]
+ utils.PrintCmd(cmd)
+ subprocess.check_output(cmd)
return temp
def is_hash(version):
@@ -248,6 +267,8 @@
min_api = determine_min_api(args, build_properties)
classfile = determine_class_file(args, build_properties)
jar = args.r8_jar if args.r8_jar else download_distribution(args, version, temp)
+ if ':' not in jar and not os.path.exists(jar):
+ error("Distribution does not exist: " + jar)
wrapper_dir = prepare_wrapper(jar, temp)
cmd = [jdk.GetJavaExecutable()]
if args.debug_agent:
@@ -261,6 +282,8 @@
cmd.append('-ea')
if args.printtimes:
cmd.append('-Dcom.android.tools.r8.printtimes=1')
+ if hasattr(args, 'properties'):
+ cmd.extend(args.properties);
cmd.extend(['-cp', '%s:%s' % (wrapper_dir, jar)])
if compiler == 'd8':
cmd.append('com.android.tools.r8.D8')
@@ -278,7 +301,7 @@
cmd.extend(['--lib', dump.library_jar()])
if dump.classpath_jar():
cmd.extend(['--classpath', dump.classpath_jar()])
- if dump.desugared_library_json():
+ if dump.desugared_library_json() and not args.disable_desugared_lib:
cmd.extend(['--desugared-lib', dump.desugared_library_json()])
if compiler != 'd8' and dump.config_file():
if hasattr(args, 'config_file_consumer') and args.config_file_consumer:
@@ -299,7 +322,7 @@
return 0
except subprocess.CalledProcessError, e:
print e.output
- if not args.nolib:
+ if not args.nolib and version != 'source':
stacktrace = os.path.join(temp, 'stacktrace')
open(stacktrace, 'w+').write(e.output)
local_map = utils.R8LIB_MAP if version == 'master' else None
diff --git a/tools/git_sync_cl_chain.py b/tools/git_sync_cl_chain.py
index 5a229bd..66713fa 100755
--- a/tools/git_sync_cl_chain.py
+++ b/tools/git_sync_cl_chain.py
@@ -126,7 +126,7 @@
has_seen_open_branch = True
has_seen_local_branch = has_seen_local_branch or (status == 'None')
- if options.upload:
+ if options.upload and status != 'closed':
if has_seen_local_branch:
print(
'Cannot upload branch %s since it comes after a local branch'
diff --git a/tools/retrace.py b/tools/retrace.py
index 3be9771..70e3099 100755
--- a/tools/retrace.py
+++ b/tools/retrace.py
@@ -40,6 +40,11 @@
'--stacktrace',
help='Path to stacktrace file.',
default=None)
+ parser.add_argument(
+ '--quiet',
+ default=None,
+ action='store_true',
+ help='Disables diagnostics printing to stdout.')
return parser.parse_args()
@@ -67,9 +72,10 @@
hash_or_version,
args.stacktrace,
args.commit_hash is not None,
- args.no_r8lib)
+ args.no_r8lib,
+ quiet=args.quiet)
-def run(map_path, hash_or_version, stacktrace, is_hash, no_r8lib):
+def run(map_path, hash_or_version, stacktrace, is_hash, no_r8lib, quiet=False):
if hash_or_version:
download_path = archive.GetUploadDestination(
hash_or_version,
@@ -93,7 +99,7 @@
if stacktrace:
retrace_args.append(stacktrace)
- utils.PrintCmd(retrace_args)
+ utils.PrintCmd(retrace_args, quiet=quiet)
return subprocess.call(retrace_args)
diff --git a/tools/run_on_app_dump.py b/tools/run_on_app_dump.py
index bf5d4c7..03687af 100755
--- a/tools/run_on_app_dump.py
+++ b/tools/run_on_app_dump.py
@@ -7,12 +7,12 @@
import apk_masseur
import compiledump
import gradle
-import jdk
import optparse
import os
import shutil
import sys
import time
+import update_prebuilds_in_android
import utils
import zipfile
@@ -38,14 +38,27 @@
defaults = {
'id': None,
'name': None,
+ 'collections': [],
'dump_app': None,
'apk_app': None,
+ 'dump_test': None,
'apk_test': None,
'skip': False,
'url': None, # url is not used but nice to have for updating apps
'revision': None,
'folder': None,
'skip_recompilation': False,
+ 'compiler_properties': [],
+ }
+ # This below does not work in python3
+ defaults.update(fields.items())
+ self.__dict__ = defaults
+
+
+class AppCollection(object):
+ def __init__(self, fields):
+ defaults = {
+ 'name': None
}
# This below does not work in python3
defaults.update(fields.items())
@@ -89,8 +102,6 @@
'url': 'https://github.com/christofferqa/AntennaPod.git',
'revision': '77e94f4783a16abe9cc5b78dc2d2b2b1867d8c06',
'folder': 'antennapod',
- # TODO(b/172450929): Fix recompilation
- 'skip_recompilation': True
}),
App({
'id': 'com.example.applymapping',
@@ -112,8 +123,9 @@
'url': 'https://github.com/mkj-gram/chanu.git',
'revision': '6e53458f167b6d78398da60c20fd0da01a232617',
'folder': 'chanu',
- # TODO(b/172535996): Fix recompilation
- 'skip_recompilation': True
+ # The app depends on a class file that has access flags interface but
+ # not abstract
+ 'compiler_properties': ['-Dcom.android.tools.r8.allowInvalidCfAccessFlags=true']
}),
# TODO(b/172539375): Monkey runner fails on recompilation.
App({
@@ -131,14 +143,12 @@
'dump_app': 'dump_app.zip',
'apk_app': 'app-debug.apk',
# TODO(b/172549283): Compiling tests fails
- 'id_test': 'com.example.applymapping.test',
+ 'id_test': 'com.google.samples.apps.sunflower.test',
'dump_test': 'dump_test.zip',
'apk_test': 'app-debug-androidTest.apk',
'url': 'https://github.com/android/sunflower',
'revision': '0c4c88fdad2a74791199dffd1a6559559b1dbd4a',
'folder': 'sunflower',
- # TODO(b/172548728): Fix recompilation
- 'skip_recompilation': True
}),
# TODO(b/172565385): Monkey runner fails on recompilation
App({
@@ -151,6 +161,175 @@
'folder': 'iosched',
}),
App({
+ 'id': 'fr.neamar.kiss',
+ 'name': 'KISS',
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'app-release.apk',
+ # TODO(b/172569220): Running tests fails due to missing keep rules
+ 'id_test': 'fr.neamar.kiss.test',
+ 'dump_test': 'dump_test.zip',
+ 'apk_test': 'app-release-androidTest.apk',
+ 'url': 'https://github.com/Neamar/KISS',
+ 'revision': '8ccffaadaf0d0b8fc4418ed2b4281a0935d3d971',
+ 'folder': 'kiss',
+ }),
+ # TODO(b/172577344): Monkey runner not working.
+ App({
+ 'id': 'io.github.hidroh.materialistic',
+ 'name': 'materialistic',
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'app-release.apk',
+ 'url': 'https://github.com/christofferqa/materialistic.git',
+ 'revision': '2b2b2ee25ce9e672d5aab1dc90a354af1522b1d9',
+ 'folder': 'materialistic',
+ }),
+ App({
+ 'id': 'com.avjindersinghsekhon.minimaltodo',
+ 'name': 'MinimalTodo',
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'app-release.apk',
+ 'url': 'https://github.com/christofferqa/Minimal-Todo',
+ 'revision': '9d8c73746762cd376b718858ec1e8783ca07ba7c',
+ 'folder': 'minimal-todo',
+ }),
+ App({
+ 'id': 'net.nurik.roman.muzei',
+ 'name': 'muzei',
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'muzei-release.apk',
+ 'url': 'https://github.com/romannurik/muzei',
+ 'revision': '9eac6e98aebeaf0ae40bdcd85f16dd2886551138',
+ 'folder': 'muzei',
+ }),
+ # TODO(b/172806281): Monkey runner does not work.
+ App({
+ 'id': 'org.schabi.newpipe',
+ 'name': 'NewPipe',
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'app-release-unsigned.apk',
+ 'url': 'https://github.com/TeamNewPipe/NewPipe',
+ 'revision': 'f4435f90313281beece70c544032f784418d85fa',
+ 'folder': 'newpipe',
+ }),
+ # TODO(b/172806808): Monkey runner does not work.
+ App({
+ 'id': 'io.rover.app.debug',
+ 'name': 'Rover',
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'example-app-release-unsigned.apk',
+ 'url': 'https://github.com/RoverPlatform/rover-android',
+ 'revision': '94342117097770ea3ca2c6df6ab496a1a55c3ce7',
+ 'folder': 'rover-android',
+ }),
+ App({
+ 'id': 'io.rover.app.debug',
+ 'name': 'Rover',
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'example-app-release-unsigned.apk',
+ 'url': 'https://github.com/RoverPlatform/rover-android',
+ 'revision': '94342117097770ea3ca2c6df6ab496a1a55c3ce7',
+ 'folder': 'rover-android',
+ }),
+ # TODO(b/172808159): Monkey runner does not work
+ App({
+ 'id': 'com.google.android.apps.santatracker',
+ 'name': 'SantaTracker',
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'santa-tracker-release.apk',
+ 'url': 'https://github.com/christofferqa/santa-tracker-android',
+ 'revision': '8dee74be7d9ee33c69465a07088c53087d24a6dd',
+ 'folder': 'santa-tracker',
+ }),
+ App({
+ 'id': 'org.thoughtcrime.securesms',
+ 'name': 'Signal',
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'Signal-Android-play-prod-universal-release-4.76.2.apk',
+ # TODO(b/172812839): Instrumentation test fails.
+ 'id_test': 'org.thoughtcrime.securesms.test',
+ 'dump_test': 'dump_test.zip',
+ 'apk_test': 'Signal-Android-play-prod-release-androidTest.apk',
+ 'url': 'https://github.com/signalapp/Signal-Android',
+ 'revision': '91ca19f294362ccee2c2b43c247eba228e2b30a1',
+ 'folder': 'signal-android',
+ }),
+ # TODO(b/172815827): Monkey runner does not work
+ App({
+ 'id': 'com.simplemobiletools.calendar.pro',
+ 'name': 'Simple-Calendar',
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'calendar-release.apk',
+ 'url': 'https://github.com/SimpleMobileTools/Simple-Calendar',
+ 'revision': '906209874d0a091c7fce5a57972472f272d6b068',
+ 'folder': 'simple-calendar',
+ }),
+ # TODO(b/172815534): Monkey runner does not work
+ App({
+ 'id': 'com.simplemobiletools.camera.pro',
+ 'name': 'Simple-Camera',
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'camera-release.apk',
+ 'url': 'https://github.com/SimpleMobileTools/Simple-Camera',
+ 'revision': 'ebf9820c51e960912b3238287e30a131244fdee6',
+ 'folder': 'simple-camera',
+ }),
+ App({
+ 'id': 'com.simplemobiletools.filemanager.pro',
+ 'name': 'Simple-File-Manager',
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'file-manager-release.apk',
+ 'url': 'https://github.com/SimpleMobileTools/Simple-File-Manager',
+ 'revision': '2b7fa68ea251222cc40cf6d62ad1de260a6f54d9',
+ 'folder': 'simple-file-manager',
+ }),
+ App({
+ 'id': 'com.simplemobiletools.gallery.pro',
+ 'name': 'Simple-Gallery',
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'gallery-326-foss-release.apk',
+ 'url': 'https://github.com/SimpleMobileTools/Simple-Gallery',
+ 'revision': '564e56b20d33b28d0018c8087ec705beeb60785e',
+ 'folder': 'simple-gallery',
+ }),
+ App({
+ 'id': 'com.example.sqldelight.hockey',
+ 'name': 'SQLDelight',
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'android-release.apk',
+ 'url': 'https://github.com/christofferqa/sqldelight',
+ 'revision': '2e67a1126b6df05e4119d1e3a432fde51d76cdc8',
+ 'folder': 'sqldelight',
+ }),
+ # TODO(b/172824096): Monkey runner does not work.
+ App({
+ 'id': 'eu.kanade.tachiyomi',
+ 'name': 'Tachiyomi',
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'app-dev-release.apk',
+ 'url': 'https://github.com/inorichi/tachiyomi',
+ 'revision': '8aa6486bf76ab9a61a5494bee284b1a5e9180bf3',
+ 'folder': 'tachiyomi',
+ }),
+ # TODO(b/172862042): Monkey runner does not work.
+ App({
+ 'id': 'app.tivi',
+ 'name': 'Tivi',
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'app-release.apk',
+ 'url': 'https://github.com/chrisbanes/tivi',
+ 'revision': '8e2ddd8fe2d343264a66aa1ef8acbd4cc587e8ce',
+ 'folder': 'tivi',
+ }),
+ App({
+ 'id': 'com.keylesspalace.tusky',
+ 'name': 'Tusky',
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'app-blue-release.apk',
+ 'url': 'https://github.com/tuskyapp/Tusky',
+ 'revision': '814a9b8f9bacf8d26f712b06a0313a3534a2be95',
+ 'folder': 'tusky',
+ }),
+ App({
'id': 'org.wikipedia',
'name': 'Wikipedia',
'dump_app': 'dump_app.zip',
@@ -159,6 +338,107 @@
'revision': '0fa7cad843c66313be8e25790ef084cf1a1fa67e',
'folder': 'wikipedia',
}),
+ # TODO(b/173167253): Check if monkey testing works.
+ App({
+ 'id': 'androidx.compose.samples.crane',
+ 'name': 'compose-crane',
+ 'collections': ['compose-samples'],
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'app-release-unsigned.apk',
+ 'url': 'https://github.com/android/compose-samples',
+ 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
+ 'folder': 'android/compose-samples/crane',
+ }),
+ # TODO(b/173167253): Check if monkey testing works.
+ App({
+ 'id': 'com.example.jetcaster',
+ 'name': 'compose-jetcaster',
+ 'collections': ['compose-samples'],
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'app-release-unsigned.apk',
+ 'url': 'https://github.com/android/compose-samples',
+ 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
+ 'folder': 'android/compose-samples/jetcaster',
+ # TODO(b/173176042): Fix recompilation
+ 'skip_recompilation': True,
+ }),
+ # TODO(b/173167253): Check if monkey testing works.
+ App({
+ 'id': 'com.example.compose.jetchat',
+ 'name': 'compose-jetchat',
+ 'collections': ['compose-samples'],
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'app-release-unsigned.apk',
+ 'url': 'https://github.com/android/compose-samples',
+ 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
+ 'folder': 'android/compose-samples/jetchat',
+ # TODO(b/173176042): Fix recompilation
+ 'skip_recompilation': True,
+ }),
+ # TODO(b/173167253): Check if monkey testing works.
+ App({
+ 'id': 'com.example.jetnews',
+ 'name': 'compose-jetnews',
+ 'collections': ['compose-samples'],
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'app-release-unsigned.apk',
+ 'url': 'https://github.com/android/compose-samples',
+ 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
+ 'folder': 'android/compose-samples/jetnews',
+ # TODO(b/173176042): Fix recompilation
+ 'skip_recompilation': True,
+ }),
+ # TODO(b/173167253): Check if monkey testing works.
+ App({
+ 'id': 'com.example.jetsnack',
+ 'name': 'compose-jetsnack',
+ 'collections': ['compose-samples'],
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'app-release-unsigned.apk',
+ 'url': 'https://github.com/android/compose-samples',
+ 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
+ 'folder': 'android/compose-samples/jetsnack',
+ }),
+ # TODO(b/173167253): Check if monkey testing works.
+ App({
+ 'id': 'com.example.compose.jetsurvey',
+ 'name': 'compose-jetsurvey',
+ 'collections': ['compose-samples'],
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'app-release-unsigned.apk',
+ 'url': 'https://github.com/android/compose-samples',
+ 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
+ 'folder': 'android/compose-samples/jetsurvey',
+ }),
+ # TODO(b/173167253): Check if monkey testing works.
+ App({
+ 'id': 'com.example.owl',
+ 'name': 'compose-owl',
+ 'collections': ['compose-samples'],
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'app-release-unsigned.apk',
+ 'url': 'https://github.com/android/compose-samples',
+ 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
+ 'folder': 'android/compose-samples/owl',
+ }),
+ # TODO(b/173167253): Check if monkey testing works.
+ App({
+ 'id': 'com.example.compose.rally',
+ 'name': 'compose-rally',
+ 'collections': ['compose-samples'],
+ 'dump_app': 'dump_app.zip',
+ 'apk_app': 'app-release-unsigned.apk',
+ 'url': 'https://github.com/android/compose-samples',
+ 'revision': '779cf9e187b8ee2c6b620b2abb4524719b3f10f8',
+ 'folder': 'android/compose-samples/rally',
+ }),
+]
+
+
+APP_COLLECTIONS = [
+ AppCollection({
+ 'name': 'compose-samples',
+ })
]
@@ -188,7 +468,11 @@
def is_full_r8(shrinker):
- return '-full' not in shrinker
+ return '-full' in shrinker
+
+
+def version_is_built_jar(version):
+ return version != 'master' and version != 'source'
def compute_size_of_dex_files_in_package(path):
@@ -208,6 +492,13 @@
return os.path.join(app_dir, app.dump_test)
+def get_r8_jar(options, temp_dir, shrinker):
+ if (options.version == 'source'):
+ return None
+ return os.path.join(
+ temp_dir, 'r8lib.jar' if is_minified_r8(shrinker) else 'r8.jar')
+
+
def get_results_for_app(app, options, temp_dir):
app_folder = app.folder if app.folder else app.name + "_" + app.revision
app_dir = os.path.join(utils.OPENSOURCE_DUMPS_DIR, app_folder)
@@ -255,7 +546,7 @@
app.name,
shrinker))
print('To compile locally: '
- 'tools/run_on_as_app.py --shrinker {} --r8-compilation-steps {} '
+ 'tools/run_on_app_dump.py --shrinker {} --r8-compilation-steps {} '
'--app {}'.format(
shrinker,
options.r8_compilation_steps,
@@ -263,7 +554,9 @@
print('HINT: use --shrinker r8-nolib --no-build if you have a local R8.jar')
recomp_jar = None
status = 'success'
- compilation_steps = 1 if app.skip_recompilation else options.r8_compilation_steps;
+ if options.r8_compilation_steps < 1:
+ return
+ compilation_steps = 1 if app.skip_recompilation else options.r8_compilation_steps
for compilation_step in range(0, compilation_steps):
if status != 'success':
break
@@ -312,7 +605,8 @@
app.id, options.emulator_id, app_apk_destination, options.monkey_events,
options.quiet, is_logging_enabled_for(app, options)) else 'failed'
- if result.get('build_status') == 'success' and options.run_tests:
+ if (result.get('build_status') == 'success'
+ and options.run_tests and app.dump_test):
if not os.path.isfile(app_apk_destination):
apk_masseur.masseur(
original_app_apk, dex=app_jar, resources='META-INF/services/*',
@@ -348,55 +642,55 @@
def build_app_with_shrinker(app, options, temp_dir, app_dir, shrinker,
compilation_step_index, compilation_steps,
prev_recomp_jar):
- r8jar = os.path.join(
- temp_dir, 'r8lib.jar' if is_minified_r8(shrinker) else 'r8.jar')
args = AttrDict({
'dump': dump_for_app(app_dir, app),
- 'r8_jar': r8jar,
+ 'r8_jar': get_r8_jar(options, temp_dir, shrinker),
'ea': False if options.disable_assertions else True,
- 'version': 'master',
+ 'version': options.version,
'compiler': 'r8full' if is_full_r8(shrinker) else 'r8',
'debug_agent': options.debug_agent,
'program_jar': prev_recomp_jar,
'nolib': not is_minified_r8(shrinker),
'config_file_consumer': remove_print_lines,
+ 'properties': app.compiler_properties,
+ 'disable_desugared_lib': False,
})
- compile_result = compiledump.run1(temp_dir, args, [])
-
- out_jar = os.path.join(temp_dir, "out.jar")
- out_mapping = os.path.join(temp_dir, "out.jar.map")
app_jar = os.path.join(
temp_dir, '{}_{}_{}_dex_out.jar'.format(
app.name, shrinker, compilation_step_index))
app_mapping = os.path.join(
temp_dir, '{}_{}_{}_dex_out.jar.map'.format(
app.name, shrinker, compilation_step_index))
-
- if compile_result != 0 or not os.path.isfile(out_jar):
- assert False, "Compilation of app_jar failed"
- shutil.move(out_jar, app_jar)
- shutil.move(out_mapping, app_mapping)
-
recomp_jar = None
- if compilation_step_index < compilation_steps - 1:
- args['classfile'] = True
- args['min_api'] = "10000"
- compile_result = compiledump.run1(temp_dir, args, [])
- if compile_result == 0:
- recomp_jar = os.path.join(
- temp_dir, '{}_{}_{}_cf_out.jar'.format(
- app.name, shrinker, compilation_step_index))
- shutil.move(out_jar, recomp_jar)
+
+ with utils.TempDir() as compile_temp_dir:
+ compile_result = compiledump.run1(compile_temp_dir, args, [])
+ out_jar = os.path.join(compile_temp_dir, "out.jar")
+ out_mapping = os.path.join(compile_temp_dir, "out.jar.map")
+
+ if compile_result != 0 or not os.path.isfile(out_jar):
+ assert False, 'Compilation of {} failed'.format(dump_for_app(app_dir, app))
+ shutil.move(out_jar, app_jar)
+ shutil.move(out_mapping, app_mapping)
+
+ if compilation_step_index < compilation_steps - 1:
+ args['classfile'] = True
+ args['min_api'] = "10000"
+ args['disable_desugared_lib'] = True
+ compile_result = compiledump.run1(compile_temp_dir, args, [])
+ if compile_result == 0:
+ recomp_jar = os.path.join(
+ temp_dir, '{}_{}_{}_cf_out.jar'.format(
+ app.name, shrinker, compilation_step_index))
+ shutil.move(out_jar, recomp_jar)
return (app_jar, app_mapping, recomp_jar)
def build_test_with_shrinker(app, options, temp_dir, app_dir, shrinker,
compilation_step_index, mapping):
- r8jar = os.path.join(
- temp_dir, 'r8lib.jar' if is_minified_r8(shrinker) else 'r8.jar')
def rewrite_file(file):
remove_print_lines(file)
@@ -410,9 +704,9 @@
args = AttrDict({
'dump': dump_test_for_app(app_dir, app),
- 'r8_jar': r8jar,
+ 'r8_jar': get_r8_jar(options, temp_dir, shrinker),
'ea': False if options.disable_assertions else True,
- 'version': 'master',
+ 'version': options.version,
'compiler': 'r8full' if is_full_r8(shrinker) else 'r8',
'debug_agent': options.debug_agent,
'nolib': not is_minified_r8(shrinker),
@@ -421,17 +715,16 @@
'config_file_consumer': rewrite_file
})
- compile_result = compiledump.run1(temp_dir, args, [])
-
- out_jar = os.path.join(temp_dir, "out.jar")
test_jar = os.path.join(
temp_dir, '{}_{}_{}_test_out.jar'.format(
app.name, shrinker, compilation_step_index))
- if compile_result != 0 or not os.path.isfile(out_jar):
- return None
-
- shutil.move(out_jar, test_jar)
+ with utils.TempDir() as compile_temp_dir:
+ compile_result = compiledump.run1(compile_temp_dir, args, [])
+ out_jar = os.path.join(compile_temp_dir, "out.jar")
+ if compile_result != 0 or not os.path.isfile(out_jar):
+ return None
+ shutil.move(out_jar, test_jar)
return test_jar
@@ -538,6 +831,10 @@
help='What app to run on',
choices=[app.name for app in APPS],
action='append')
+ result.add_option('--app-collection', '--app_collection',
+ help='What app collection to run',
+ choices=[collection.name for collection in APP_COLLECTIONS],
+ action='append')
result.add_option('--bot',
help='Running on bot, use third_party dependency.',
default=False,
@@ -610,13 +907,26 @@
help='The shrinkers to use (by default, all are run)',
action='append')
result.add_option('--version',
+ default='master',
help='The version of R8 to use (e.g., 1.4.51)')
(options, args) = result.parse_args(argv)
- if options.app:
- options.apps = [app for app in APPS if app.name in options.app]
+
+ if options.app or options.app_collection:
+ if not options.app:
+ options.app = []
+ if not options.app_collection:
+ options.app_collection = []
+ options.apps = [
+ app
+ for app in APPS
+ if app.name in options.app
+ or any(collection in options.app_collection
+ for collection in app.collections)]
del options.app
+ del options.app_collection
else:
options.apps = APPS
+
if options.app_logging_filter:
for app_name in options.app_logging_filter:
assert any(app.name == app_name for app in options.apps)
@@ -626,7 +936,7 @@
else:
options.shrinker = [shrinker for shrinker in SHRINKERS]
- if options.hash or options.version:
+ if options.hash or version_is_built_jar(options.version):
# No need to build R8 if a specific version should be used.
options.no_build = True
if 'r8-nolib' in options.shrinker:
@@ -666,16 +976,16 @@
as_utils.MoveFile(
os.path.join(temp_dir, target), os.path.join(temp_dir, 'r8lib.jar'),
quiet=options.quiet)
- elif options.version:
- # Download r8-<version>.jar from
- # https://storage.googleapis.com/r8-releases/raw/.
- target = 'r8-{}.jar'.format(options.version)
- update_prebuilds_in_android.download_version(
- temp_dir, 'com/android/tools/r8/' + options.version, target)
- as_utils.MoveFile(
- os.path.join(temp_dir, target), os.path.join(temp_dir, 'r8lib.jar'),
- quiet=options.quiet)
- else:
+ elif version_is_built_jar(options.version):
+ # Download r8-<version>.jar from
+ # https://storage.googleapis.com/r8-releases/raw/.
+ target = 'r8-{}.jar'.format(options.version)
+ update_prebuilds_in_android.download_version(
+ temp_dir, 'com/android/tools/r8/' + options.version, target)
+ as_utils.MoveFile(
+ os.path.join(temp_dir, target), os.path.join(temp_dir, 'r8lib.jar'),
+ quiet=options.quiet)
+ elif options.version == 'master':
if not (options.no_build or options.golem):
gradle.RunGradle(['r8', '-Pno_internal'])
build_r8lib = False
@@ -699,7 +1009,12 @@
continue
result_per_shrinker_per_app.append(
(app, get_results_for_app(app, options, temp_dir)))
- return log_results_for_apps(result_per_shrinker_per_app, options)
+ errors = log_results_for_apps(result_per_shrinker_per_app, options)
+ if errors > 0:
+ dest = 'gs://r8-test-results/r8-libs/' + str(int(time.time()))
+ utils.upload_file_to_cloud_storage(os.path.join(temp_dir, 'r8lib.jar'), dest)
+ print('R8lib saved to %s' % dest)
+ return errors
def success(message):
diff --git a/tools/test.py b/tools/test.py
index d7987a7..0c70124 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -149,6 +149,13 @@
' and empty (for no runtimes).')
result.add_option('--print-hanging-stacks', '--print_hanging_stacks',
default=-1, type="int", help='Print hanging stacks after timeout in seconds')
+ result.add_option('--print-full-stacktraces', '--print_full_stacktraces',
+ default=False, action='store_true',
+ help='Print the full stacktraces without any filtering applied')
+ result.add_option(
+ '--print-obfuscated-stacktraces', '--print_obfuscated_stacktraces',
+ default=False, action='store_true',
+ help='Print the obfuscated stacktraces')
return result.parse_args()
def archive_failures():
@@ -202,6 +209,10 @@
gradle_args.append('-Pdisable_assertions')
if options.with_code_coverage:
gradle_args.append('-Pwith_code_coverage')
+ if options.print_full_stacktraces:
+ gradle_args.append('-Pprint_full_stacktraces')
+ if options.print_obfuscated_stacktraces:
+ gradle_args.append('-Pprint_obfuscated_stacktraces')
if os.name == 'nt':
# temporary hack
gradle_args.append('-Pno_internal')
diff --git a/tools/utils.py b/tools/utils.py
index 0ece764..5396c0a 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -29,6 +29,7 @@
BUILD = os.path.join(REPO_ROOT, 'build')
BUILD_DEPS_DIR = os.path.join(BUILD, 'deps')
BUILD_MAIN_DIR = os.path.join(BUILD, 'classes', 'main')
+BUILD_JAVA_MAIN_DIR = os.path.join(BUILD, 'classes', 'java', 'main')
BUILD_TEST_DIR = os.path.join(BUILD, 'classes', 'test')
LIBS = os.path.join(BUILD, 'libs')
GENERATED_LICENSE_DIR = os.path.join(BUILD, 'generatedLicense')
@@ -43,6 +44,7 @@
R8_SRC = 'sourceJar'
LIBRARY_DESUGAR_CONVERSIONS = 'buildLibraryDesugarConversions'
+ALL_DEPS_JAR = os.path.join(LIBS, 'deps_all.jar')
D8_JAR = os.path.join(LIBS, 'd8.jar')
R8_JAR = os.path.join(LIBS, 'r8.jar')
R8LIB_JAR = os.path.join(LIBS, 'r8lib.jar')