Merge commit '021c3f09703d17fec5022a007125dbf3e06a285e' into dev-release
diff --git a/.gitignore b/.gitignore
index f16c1ba..80c9662 100644
--- a/.gitignore
+++ b/.gitignore
@@ -131,6 +131,12 @@
third_party/openjdk/desugar_jdk_libs_releases/1.1.1.tar.gz
third_party/openjdk/desugar_jdk_libs_releases/1.1.5
third_party/openjdk/desugar_jdk_libs_releases/1.1.5.tar.gz
+third_party/openjdk/jdk-16/linux
+third_party/openjdk/jdk-16/linux.tar.gz
+third_party/openjdk/jdk-16/osx
+third_party/openjdk/jdk-16/osx.tar.gz
+third_party/openjdk/jdk-16/windows
+third_party/openjdk/jdk-16/windows.tar.gz
third_party/openjdk/jdk-15/linux
third_party/openjdk/jdk-15/linux.tar.gz
third_party/openjdk/jdk-15/osx
diff --git a/build.gradle b/build.gradle
index 17206b1..4a8dd81 100644
--- a/build.gradle
+++ b/build.gradle
@@ -131,9 +131,9 @@
srcDirs = ['src/test/examplesJava11']
}
}
- examplesJava15 {
+ examplesJava16 {
java {
- srcDirs = ['src/test/examplesJava15']
+ srcDirs = ['src/test/examplesJava16']
}
}
jdk11TimeTests {
@@ -372,18 +372,18 @@
"third_party": ["openjdk/openjdk-9.0.4/linux",
"openjdk/jdk8/linux-x86",
"openjdk/jdk-11/linux",
- "openjdk/jdk-15/linux"],
+ "openjdk/jdk-16/linux"],
],
osx: [
"third_party": ["openjdk/openjdk-9.0.4/osx",
"openjdk/jdk8/darwin-x86",
"openjdk/jdk-11/osx",
- "openjdk/jdk-15/osx"],
+ "openjdk/jdk-16/osx"],
],
windows: [
"third_party": ["openjdk/openjdk-9.0.4/windows",
"openjdk/jdk-11/windows",
- "openjdk/jdk-15/windows"],
+ "openjdk/jdk-16/windows"],
],
]
@@ -575,12 +575,15 @@
options.compilerArgs.add('--enable-preview')
}
if (OperatingSystem.current().isLinux()) {
+ dependsOn getDownloadDepsTaskName("third_party", "openjdk/" + javaHome + "/linux")
options.forkOptions.javaHome = file(jdkDir + 'linux')
} else if (OperatingSystem.current().isMacOsX()) {
+ dependsOn getDownloadDepsTaskName("third_party", "openjdk/" + javaHome + "/osx")
options.forkOptions.javaHome = compatibility > JavaVersion.VERSION_1_9
? file(jdkDir + 'osx/Contents/Home')
: file(jdkDir + 'osx')
} else {
+ dependsOn getDownloadDepsTaskName("third_party", "openjdk/" + javaHome + "/windows")
options.forkOptions.javaHome = file(jdkDir + 'windows')
}
sourceCompatibility = compatibility
@@ -619,9 +622,9 @@
JavaVersion.VERSION_11,
false)
setJdkCompilationWithCompatibility(
- sourceSets.examplesJava15.compileJavaTaskName,
- 'jdk-15',
- JavaVersion.VERSION_15,
+ sourceSets.examplesJava16.compileJavaTaskName,
+ 'jdk-16',
+ JavaVersion.VERSION_16,
true)
task compileMainWithJava11 (type: JavaCompile) {
@@ -1591,7 +1594,7 @@
buildExampleJarsCreateTask("Java9", sourceSets.examplesJava9)
buildExampleJarsCreateTask("Java10", sourceSets.examplesJava10)
buildExampleJarsCreateTask("Java11", sourceSets.examplesJava11)
-buildExampleJarsCreateTask("Java15", sourceSets.examplesJava15)
+buildExampleJarsCreateTask("Java16", sourceSets.examplesJava16)
task provideArtFrameworksDependencies {
cloudDependencies.tools.forEach({ art ->
@@ -1674,7 +1677,7 @@
dependsOn buildExampleJava9Jars
dependsOn buildExampleJava10Jars
dependsOn buildExampleJava11Jars
- dependsOn buildExampleJava15Jars
+ dependsOn buildExampleJava16Jars
dependsOn buildExampleAndroidApi
def examplesDir = file("src/test/examples")
def noDexTests = [
@@ -2046,7 +2049,9 @@
// Hide all test events from the console, they are written to the report.
task.testLogging { events = [] }
- def branch = getGitBranchName()
+ def branch = project.hasProperty('testing-state-name')
+ ? project.getProperty('testing-state-name')
+ : getGitBranchName()
def reportDir = file("${buildDir}/test-state/${branch}")
def index = reportDir.toPath().resolve("index.html").toFile()
def resetState = project.hasProperty('reset-testing-state')
diff --git a/infra/config/global/generated/cr-buildbucket.cfg b/infra/config/global/generated/cr-buildbucket.cfg
index 8caf056..b881778 100644
--- a/infra/config/global/generated/cr-buildbucket.cfg
+++ b/infra/config/global/generated/cr-buildbucket.cfg
@@ -40,6 +40,28 @@
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
}
builders {
+ name: "archive_lib_desugar"
+ swarming_host: "chrome-swarming.appspot.com"
+ swarming_tags: "vpython:native-python-wrapper"
+ dimensions: "cores:8"
+ dimensions: "cpu:x86-64"
+ dimensions: "os:Ubuntu-16.04"
+ dimensions: "pool:luci.r8.ci"
+ recipe {
+ name: "rex"
+ cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
+ cipd_version: "refs/heads/master"
+ properties_j: "archive:\"true\""
+ properties_j: "builder_group:\"internal.client.r8\""
+ properties_j: "sdk_desugar:\"true\""
+ }
+ priority: 25
+ execution_timeout_secs: 3600
+ expiration_secs: 126000
+ build_numbers: YES
+ service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+ }
+ builders {
name: "archive_release"
swarming_host: "chrome-swarming.appspot.com"
swarming_tags: "vpython:native-python-wrapper"
@@ -413,6 +435,54 @@
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
}
builders {
+ name: "linux-d8_jctf"
+ swarming_host: "chrome-swarming.appspot.com"
+ swarming_tags: "vpython:native-python-wrapper"
+ dimensions: "cores:8"
+ dimensions: "cpu:x86-64"
+ dimensions: "jctf:true"
+ dimensions: "os:Ubuntu-16.04"
+ dimensions: "pool:luci.r8.ci"
+ recipe {
+ name: "rex"
+ cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
+ cipd_version: "refs/heads/master"
+ properties_j: "builder_group:\"internal.client.r8\""
+ properties_j: "dex_vm:\"all\""
+ properties_j: "only_jctf:\"true\""
+ properties_j: "tool:\"d8\""
+ }
+ priority: 26
+ execution_timeout_secs: 43200
+ expiration_secs: 126000
+ build_numbers: YES
+ service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+ }
+ builders {
+ name: "linux-d8_jctf_release"
+ swarming_host: "chrome-swarming.appspot.com"
+ swarming_tags: "vpython:native-python-wrapper"
+ dimensions: "cores:8"
+ dimensions: "cpu:x86-64"
+ dimensions: "jctf:true"
+ dimensions: "os:Ubuntu-16.04"
+ dimensions: "pool:luci.r8.ci"
+ recipe {
+ name: "rex"
+ cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
+ cipd_version: "refs/heads/master"
+ properties_j: "builder_group:\"internal.client.r8\""
+ properties_j: "dex_vm:\"all\""
+ properties_j: "only_jctf:\"true\""
+ properties_j: "tool:\"d8\""
+ }
+ priority: 26
+ execution_timeout_secs: 43200
+ expiration_secs: 126000
+ build_numbers: YES
+ service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+ }
+ builders {
name: "linux-dex_default"
swarming_host: "chrome-swarming.appspot.com"
swarming_tags: "vpython:native-python-wrapper"
@@ -677,6 +747,54 @@
service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
}
builders {
+ name: "linux-r8cf_jctf"
+ swarming_host: "chrome-swarming.appspot.com"
+ swarming_tags: "vpython:native-python-wrapper"
+ dimensions: "cores:8"
+ dimensions: "cpu:x86-64"
+ dimensions: "jctf:true"
+ dimensions: "os:Ubuntu-16.04"
+ dimensions: "pool:luci.r8.ci"
+ recipe {
+ name: "rex"
+ cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
+ cipd_version: "refs/heads/master"
+ properties_j: "builder_group:\"internal.client.r8\""
+ properties_j: "dex_vm:\"all\""
+ properties_j: "only_jctf:\"true\""
+ properties_j: "tool:\"r8cf\""
+ }
+ priority: 26
+ execution_timeout_secs: 43200
+ expiration_secs: 126000
+ build_numbers: YES
+ service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+ }
+ builders {
+ name: "linux-r8cf_jctf_release"
+ swarming_host: "chrome-swarming.appspot.com"
+ swarming_tags: "vpython:native-python-wrapper"
+ dimensions: "cores:8"
+ dimensions: "cpu:x86-64"
+ dimensions: "jctf:true"
+ dimensions: "os:Ubuntu-16.04"
+ dimensions: "pool:luci.r8.ci"
+ recipe {
+ name: "rex"
+ cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
+ cipd_version: "refs/heads/master"
+ properties_j: "builder_group:\"internal.client.r8\""
+ properties_j: "dex_vm:\"all\""
+ properties_j: "only_jctf:\"true\""
+ properties_j: "tool:\"r8cf\""
+ }
+ priority: 26
+ execution_timeout_secs: 43200
+ expiration_secs: 126000
+ build_numbers: YES
+ service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+ }
+ builders {
name: "windows"
swarming_host: "chrome-swarming.appspot.com"
swarming_tags: "vpython:native-python-wrapper"
diff --git a/infra/config/global/generated/luci-milo.cfg b/infra/config/global/generated/luci-milo.cfg
index 112e9a1..58474a2 100644
--- a/infra/config/global/generated/luci-milo.cfg
+++ b/infra/config/global/generated/luci-milo.cfg
@@ -11,6 +11,26 @@
refs: "regexp:regexp:refs/heads/.*"
manifest_name: "REVISION"
builders {
+ name: "buildbucket/luci.r8.ci/linux-d8_jctf"
+ category: "R8"
+ short_name: "d8_jctf"
+ }
+ builders {
+ name: "buildbucket/luci.r8.ci/linux-r8cf_jctf"
+ category: "R8"
+ short_name: "r8cf_jctf"
+ }
+ builders {
+ name: "buildbucket/luci.r8.ci/linux-d8_jctf_release"
+ category: "R8 release"
+ short_name: "d8_jctf_release"
+ }
+ builders {
+ name: "buildbucket/luci.r8.ci/linux-r8cf_jctf_release"
+ category: "R8 release"
+ short_name: "r8cf_jctf_release"
+ }
+ builders {
name: "buildbucket/luci.r8.ci/archive"
category: "R8"
short_name: "archive"
@@ -21,6 +41,11 @@
short_name: "archive_release"
}
builders {
+ name: "buildbucket/luci.r8.ci/archive_lib_desugar"
+ category: "R8"
+ short_name: "archive_lib_desugar"
+ }
+ builders {
name: "buildbucket/luci.r8.ci/linux-internal"
category: "R8"
short_name: "internal"
diff --git a/infra/config/global/generated/luci-notify.cfg b/infra/config/global/generated/luci-notify.cfg
index 5267938..c441677 100644
--- a/infra/config/global/generated/luci-notify.cfg
+++ b/infra/config/global/generated/luci-notify.cfg
@@ -228,6 +228,30 @@
}
builders {
bucket: "ci"
+ name: "linux-d8_jctf"
+ repository: "https://r8.googlesource.com/r8"
+ }
+}
+notifiers {
+ notifications {
+ on_failure: true
+ on_new_failure: true
+ notify_blamelist {}
+ }
+ builders {
+ bucket: "ci"
+ name: "linux-d8_jctf_release"
+ repository: "https://r8.googlesource.com/r8"
+ }
+}
+notifiers {
+ notifications {
+ on_failure: true
+ on_new_failure: true
+ notify_blamelist {}
+ }
+ builders {
+ bucket: "ci"
name: "linux-dex_default"
repository: "https://r8.googlesource.com/r8"
}
@@ -372,6 +396,30 @@
}
builders {
bucket: "ci"
+ name: "linux-r8cf_jctf"
+ repository: "https://r8.googlesource.com/r8"
+ }
+}
+notifiers {
+ notifications {
+ on_failure: true
+ on_new_failure: true
+ notify_blamelist {}
+ }
+ builders {
+ bucket: "ci"
+ name: "linux-r8cf_jctf_release"
+ repository: "https://r8.googlesource.com/r8"
+ }
+}
+notifiers {
+ notifications {
+ on_failure: true
+ on_new_failure: true
+ notify_blamelist {}
+ }
+ builders {
+ bucket: "ci"
name: "windows"
repository: "https://r8.googlesource.com/r8"
}
diff --git a/infra/config/global/generated/luci-scheduler.cfg b/infra/config/global/generated/luci-scheduler.cfg
index 4aa3c0f..b4ee1f0 100644
--- a/infra/config/global/generated/luci-scheduler.cfg
+++ b/infra/config/global/generated/luci-scheduler.cfg
@@ -19,6 +19,20 @@
}
}
job {
+ id: "archive_lib_desugar"
+ acl_sets: "ci"
+ triggering_policy {
+ kind: GREEDY_BATCHING
+ max_concurrent_invocations: 3
+ max_batch_size: 1
+ }
+ buildbucket {
+ server: "cr-buildbucket.appspot.com"
+ bucket: "luci.r8.ci"
+ builder: "archive_lib_desugar"
+ }
+}
+job {
id: "archive_release"
acl_sets: "ci"
triggering_policy {
@@ -177,6 +191,24 @@
}
}
job {
+ id: "linux-d8_jctf"
+ acl_sets: "ci"
+ buildbucket {
+ server: "cr-buildbucket.appspot.com"
+ bucket: "luci.r8.ci"
+ builder: "linux-d8_jctf"
+ }
+}
+job {
+ id: "linux-d8_jctf_release"
+ acl_sets: "ci"
+ buildbucket {
+ server: "cr-buildbucket.appspot.com"
+ bucket: "luci.r8.ci"
+ builder: "linux-d8_jctf_release"
+ }
+}
+job {
id: "linux-dex_default"
acl_sets: "ci"
buildbucket {
@@ -295,6 +327,24 @@
}
}
job {
+ id: "linux-r8cf_jctf"
+ acl_sets: "ci"
+ buildbucket {
+ server: "cr-buildbucket.appspot.com"
+ bucket: "luci.r8.ci"
+ builder: "linux-r8cf_jctf"
+ }
+}
+job {
+ id: "linux-r8cf_jctf_release"
+ acl_sets: "ci"
+ buildbucket {
+ server: "cr-buildbucket.appspot.com"
+ bucket: "luci.r8.ci"
+ builder: "linux-r8cf_jctf_release"
+ }
+}
+job {
id: "windows"
acl_sets: "ci"
buildbucket {
@@ -324,12 +374,14 @@
triggers: "linux-android=10.0.0_release"
triggers: "linux-android=8.1.0_release"
triggers: "linux-android=9.0.0_release"
+ triggers: "linux-d8_jctf_release"
triggers: "linux-dex_default_release"
triggers: "linux-internal_release"
triggers: "linux-jdk11_release"
triggers: "linux-jdk8_release"
triggers: "linux-jdk9_release"
triggers: "linux-none_release"
+ triggers: "linux-r8cf_jctf_release"
triggers: "windows_release"
gitiles {
repo: "https://r8.googlesource.com/r8"
@@ -349,12 +401,14 @@
triggers: "linux-android=10.0.0"
triggers: "linux-android=8.1.0"
triggers: "linux-android=9.0.0"
+ triggers: "linux-d8_jctf"
triggers: "linux-dex_default"
triggers: "linux-internal"
triggers: "linux-jdk11"
triggers: "linux-jdk8"
triggers: "linux-jdk9"
triggers: "linux-none"
+ triggers: "linux-r8cf_jctf"
triggers: "windows"
gitiles {
repo: "https://r8.googlesource.com/r8"
diff --git a/infra/config/global/main.star b/infra/config/global/main.star
index 77d9887..e78b09a 100755
--- a/infra/config/global/main.star
+++ b/infra/config/global/main.star
@@ -107,9 +107,9 @@
dimensions["normal"] = "true"
return dimensions
-def r8_builder(name, priority=26, **kwargs):
+def r8_builder(name, priority=26, trigger=True, **kwargs):
release = name.endswith("release")
- triggered = ["branch-gitiles-trigger"] if release \
+ triggered = None if not trigger else ["branch-gitiles-trigger"] if release\
else ["main-gitiles-trigger"]
luci.builder(
@@ -119,7 +119,7 @@
"iam.gserviceaccount.com",
build_numbers = True,
swarming_tags = ["vpython:native-python-wrapper"],
- notifies = ["r8-failures"],
+ notifies = ["r8-failures"] if trigger else None,
priority = priority,
triggered_by = triggered,
executable = "rex",
@@ -137,8 +137,8 @@
for name in [name, name + "_release"]:
r8_builder(
name = name,
- execution_timeout=execution_timeout,
- expiration_timeout=expiration_timeout,
+ execution_timeout = execution_timeout,
+ expiration_timeout = expiration_timeout,
dimensions = dimensions,
properties = {
"test_options" : test_options,
@@ -149,8 +149,36 @@
def r8_tester_with_default(name, test_options, dimensions=None):
r8_tester(name, test_options + common_test_options, dimensions)
+def jctf():
+ for release in ["", "_release"]:
+ for tool in ["d8", "r8cf"]:
+ properties = {
+ "tool": tool,
+ "builder_group" : "internal.client.r8",
+ "dex_vm" : "all",
+ "only_jctf" : "true",
+ }
+ name = "linux-" + tool + "_jctf"
+ name = name + release
+ r8_builder(
+ name,
+ dimensions = get_dimensions(jctf=True),
+ execution_timeout = time.hour * 12,
+ expiration_timeout = time.hour * 35,
+ properties = properties,
+ )
+jctf()
+
+
def archivers():
- for name in ["archive", "archive_release"]:
+ for name in ["archive", "archive_release", "archive_lib_desugar"]:
+ desugar = "desugar" in name
+ properties = {
+ "archive": "true",
+ "builder_group" : "internal.client.r8"
+ }
+ if desugar:
+ properties["sdk_desugar"] = "true"
r8_builder(
name,
dimensions = get_dimensions(),
@@ -160,11 +188,9 @@
max_concurrent_invocations = 3
),
priority = 25,
- properties = {
- "archive": "true",
- "builder_group" : "internal.client.r8"
- },
- execution_timeout = time.minute * 30,
+ trigger = not desugar,
+ properties = properties,
+ execution_timeout = time.hour * 1 if desugar else time.minute * 30 ,
expiration_timeout = time.hour * 35,
)
archivers()
diff --git a/scripts/add-openjdk.sh b/scripts/add-openjdk.sh
new file mode 100644
index 0000000..8587b93
--- /dev/null
+++ b/scripts/add-openjdk.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+#
+# Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+set -e
+set -x
+
+echo "Update this script manually before using"
+exit -1
+
+# Download JDK from https://jdk.java.net/X/ (X = version) into ~/Downloads
+# Create directory third_party/openjdk/jdk-X
+# cd into third_party/openjdk/jdk-X
+# Prepare README.google
+# Update JDK_VERSION below
+
+# Now run script wit fingers crossed!
+
+JDK_VERSION=16.0.2
+
+tar xf ~/Downloads/openjdk-${JDK_VERSION}_linux-x64_bin.tar.gz
+cp -rL jdk-${JDK_VERSION} linux
+cp README.google linux
+upload_to_google_storage.py -a --bucket r8-deps linux
+rm -rf jdk-${JDK_VERSION}
+rm -rf linux
+rm linux.tar.gz
+
+tar xf ~/Downloads/openjdk-${JDK_VERSION}_osx-x64_bin.tar.gz
+cp -rL jdk-${JDK_VERSION}.jdk osx
+cp README.google osx
+upload_to_google_storage.py -a --bucket r8-deps osx
+rm -rf osx
+rm -rf jdk-${JDK_VERSION}.jdk
+rm osx.tar.gz
+
+unzip ~/Downloads/openjdk-${JDK_VERSION}_windows-x64_bin.zip
+cp -rL jdk-${JDK_VERSION} windows
+cp README.google windows
+upload_to_google_storage.py -a --bucket r8-deps windows
+rm -rf windows
+rm -rf jdk-${JDK_VERSION}
+rm windows.tar.gz
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index c23b48c..671fb6b 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -454,6 +454,9 @@
internal.enableSwitchRewriting = false;
assert internal.enableStringSwitchConversion;
internal.enableStringSwitchConversion = false;
+ } else {
+ assert !internal.desugarSpecificOptions().allowAllDesugaredInput
+ || getDesugarState() == DesugarState.OFF;
}
internal.mainDexListConsumer = getMainDexListConsumer();
internal.minimalMainDex = internal.debug || minimalMainDex;
diff --git a/src/main/java/com/android/tools/r8/DiagnosticsHandler.java b/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
index 295d52d..8d73909 100644
--- a/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
+++ b/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
+import java.io.PrintStream;
/**
* A DiagnosticsHandler can be provided to customize handling of diagnostics information.
@@ -14,22 +15,27 @@
@Keep
public interface DiagnosticsHandler {
+ /** Should be considered private. */
+ static void printDiagnosticToStream(Diagnostic diagnostic, String prefix, PrintStream stream) {
+ if (diagnostic.getOrigin() != Origin.unknown()) {
+ stream.print(prefix + " in " + diagnostic.getOrigin());
+ if (diagnostic.getPosition() != Position.UNKNOWN) {
+ stream.print(" at " + diagnostic.getPosition().getDescription());
+ }
+ stream.println(":");
+ } else {
+ stream.print(prefix + ": ");
+ }
+ stream.println(diagnostic.getDiagnosticMessage());
+ }
+
/**
* Handle error diagnostics.
*
* @param error Diagnostic containing error information.
*/
default void error(Diagnostic error) {
- if (error.getOrigin() != Origin.unknown()) {
- System.err.print("Error in " + error.getOrigin());
- if (error.getPosition() != Position.UNKNOWN) {
- System.err.print(" at " + error.getPosition().getDescription());
- }
- System.err.println(":");
- } else {
- System.err.print("Error: ");
- }
- System.err.println(error.getDiagnosticMessage());
+ printDiagnosticToStream(error, "Error", System.err);
}
/**
@@ -38,13 +44,7 @@
* @param warning Diagnostic containing warning information.
*/
default void warning(Diagnostic warning) {
- if (warning.getOrigin() != Origin.unknown()) {
- System.err.println("Warning in " + warning.getOrigin() + ":");
- System.err.print(" ");
- } else {
- System.err.print("Warning: ");
- }
- System.err.println(warning.getDiagnosticMessage());
+ printDiagnosticToStream(warning, "Warning", System.err);
}
/**
@@ -53,13 +53,7 @@
* @param info Diagnostic containing the information.
*/
default void info(Diagnostic info) {
- if (info.getOrigin() != Origin.unknown()) {
- System.out.println("Info in " + info.getOrigin() + ":");
- System.out.print(" ");
- } else {
- System.out.print("Info: ");
- }
- System.out.println(info.getDiagnosticMessage());
+ printDiagnosticToStream(info, "Info", System.out);
}
/**
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java
index dc284ed..cba8b55 100644
--- a/src/main/java/com/android/tools/r8/PrintUses.java
+++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -23,8 +23,8 @@
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueArray;
import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.shaking.MainDexInfo;
@@ -105,7 +105,7 @@
@Override
public void registerInvokeVirtual(DexMethod method) {
- ResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method);
+ MethodResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method);
DexEncodedMethod target =
resolutionResult.isVirtualTarget() ? resolutionResult.getSingleTarget() : null;
if (target != null && target.getReference() != method) {
@@ -277,7 +277,7 @@
// If clazz overrides any methods in superType, we should keep those as well.
clazz.forEachMethod(
method -> {
- ResolutionResult resolutionResult =
+ MethodResolutionResult resolutionResult =
appInfo.resolveMethodOn(
superType, method.getReference(), superType != clazz.superType);
DexEncodedMethod dexEncodedMethod = resolutionResult.getSingleTarget();
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 6f2675f..22308ef 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -367,9 +367,9 @@
subtypingInfo,
classMergingEnqueuerExtensionBuilder);
- assert appView.rootSet().verifyKeptFieldsAreAccessedAndLive(appViewWithLiveness.appInfo());
- assert appView.rootSet().verifyKeptMethodsAreTargetedAndLive(appViewWithLiveness.appInfo());
- assert appView.rootSet().verifyKeptTypesAreLive(appViewWithLiveness.appInfo());
+ assert appView.rootSet().verifyKeptFieldsAreAccessedAndLive(appViewWithLiveness);
+ assert appView.rootSet().verifyKeptMethodsAreTargetedAndLive(appViewWithLiveness);
+ assert appView.rootSet().verifyKeptTypesAreLive(appViewWithLiveness);
assert appView.rootSet().verifyKeptItemsAreKept(appView);
appView.rootSet().checkAllRulesAreUsed(options);
diff --git a/src/main/java/com/android/tools/r8/SwissArmyKnife.java b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
index ddc81b6..0460075 100644
--- a/src/main/java/com/android/tools/r8/SwissArmyKnife.java
+++ b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
@@ -4,6 +4,7 @@
package com.android.tools.r8;
import com.android.tools.r8.bisect.Bisect;
+import com.android.tools.r8.cf.CfVerifierTool;
import com.android.tools.r8.compatproguard.CompatProguard;
import com.android.tools.r8.dexsplitter.DexSplitter;
import com.android.tools.r8.relocator.RelocatorCommandLine;
@@ -74,10 +75,13 @@
BackportedMethodList.main(shift(args));
break;
case "relocator":
- RelocatorCommandLine.main(shift((args)));
+ RelocatorCommandLine.main(shift(args));
break;
case "tracereferences":
- TraceReferences.main(shift((args)));
+ TraceReferences.main(shift(args));
+ break;
+ case "verify":
+ CfVerifierTool.main(shift(args));
break;
default:
runDefault(args);
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java
index ee0ffb2..ed6767a 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.androidapi;
+import static com.android.tools.r8.utils.AndroidApiLevel.getAndroidApiLevel;
+
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
@@ -28,38 +30,56 @@
public abstract int getMemberCount();
- public abstract TraversalContinuation visitFields(
- BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor);
+ public TraversalContinuation visitFields(
+ BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor) {
+ return visitFields(visitor, classReference, 1);
+ }
- public abstract TraversalContinuation visitMethods(
- BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor);
+ public TraversalContinuation visitMethods(
+ BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor) {
+ return visitMethods(visitor, classReference, 1);
+ }
+
+ protected abstract TraversalContinuation visitFields(
+ BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor,
+ ClassReference holder,
+ int minApiClass);
+
+ protected abstract TraversalContinuation visitMethods(
+ BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor,
+ ClassReference holder,
+ int minApiClass);
protected TraversalContinuation visitField(
+ BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor,
+ ClassReference holder,
+ int minApiClass,
+ int minApiField,
String name,
- String typeDescriptor,
- int apiLevel,
- BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor) {
+ String typeDescriptor) {
return visitor.apply(
- Reference.field(classReference, name, Reference.typeFromDescriptor(typeDescriptor)),
- AndroidApiLevel.getAndroidApiLevel(apiLevel));
+ Reference.field(holder, name, Reference.typeFromDescriptor(typeDescriptor)),
+ getAndroidApiLevel(Integer.max(minApiClass, minApiField)));
}
protected TraversalContinuation visitMethod(
+ BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor,
+ ClassReference holder,
+ int minApiClass,
+ int minApiMethod,
String name,
String[] formalTypeDescriptors,
- String returnType,
- int apiLevel,
- BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor) {
+ String returnType) {
List<TypeReference> typeReferenceList = new ArrayList<>(formalTypeDescriptors.length);
for (String formalTypeDescriptor : formalTypeDescriptors) {
typeReferenceList.add(Reference.typeFromDescriptor(formalTypeDescriptor));
}
return visitor.apply(
Reference.method(
- classReference,
+ holder,
name,
typeReferenceList,
returnType == null ? null : Reference.returnTypeFromDescriptor(returnType)),
- AndroidApiLevel.getAndroidApiLevel(apiLevel));
+ getAndroidApiLevel(Integer.max(minApiClass, minApiMethod)));
}
}
diff --git a/src/main/java/com/android/tools/r8/cf/CfVerifierTool.java b/src/main/java/com/android/tools/r8/cf/CfVerifierTool.java
new file mode 100644
index 0000000..7bf11e0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/CfVerifierTool.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.cf;
+
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppServices;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.AndroidApp.Builder;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+import java.io.IOException;
+import java.nio.file.Paths;
+
+/** Tool to verify various aspects of class file inputs. */
+public class CfVerifierTool {
+
+ public static void main(String[] args) throws IOException {
+ Builder builder = AndroidApp.builder();
+ for (String arg : args) {
+ builder.addProgramFile(Paths.get(arg));
+ }
+ InternalOptions options = new InternalOptions();
+ DexApplication dexApplication =
+ new ApplicationReader(builder.build(), options, Timing.empty()).read();
+ AppView<AppInfo> appView = AppView.createForD8(AppInfo.createInitialAppInfo(dexApplication));
+ appView.setAppServices(AppServices.builder(appView).build());
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ clazz.forEachProgramMethod(
+ method ->
+ method
+ .getDefinition()
+ .getCode()
+ .asCfCode()
+ .verifyFrames(method.getDefinition(), appView, clazz.getOrigin()));
+ }
+ }
+}
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 47eebe9..5b877b8 100644
--- a/src/main/java/com/android/tools/r8/cf/CfVersion.java
+++ b/src/main/java/com/android/tools/r8/cf/CfVersion.java
@@ -25,10 +25,17 @@
public static final CfVersion V9 = new CfVersion(Opcodes.V9);
public static final CfVersion V10 = new CfVersion(Opcodes.V10);
public static final CfVersion V11 = new CfVersion(Opcodes.V11);
+ public static final CfVersion V11_PREVIEW = new CfVersion(Opcodes.V11 | Opcodes.V_PREVIEW);
public static final CfVersion V12 = new CfVersion(Opcodes.V12);
+ public static final CfVersion V12_PREVIEW = new CfVersion(Opcodes.V12 | Opcodes.V_PREVIEW);
public static final CfVersion V13 = new CfVersion(Opcodes.V13);
+ public static final CfVersion V13_PREVIEW = new CfVersion(Opcodes.V13 | Opcodes.V_PREVIEW);
public static final CfVersion V14 = new CfVersion(Opcodes.V14);
+ public static final CfVersion V14_PREVIEW = new CfVersion(Opcodes.V14 | Opcodes.V_PREVIEW);
public static final CfVersion V15 = new CfVersion(Opcodes.V15);
+ public static final CfVersion V15_PREVIEW = new CfVersion(Opcodes.V15 | Opcodes.V_PREVIEW);
+ public static final CfVersion V16 = new CfVersion(Opcodes.V16);
+ public static final CfVersion V16_PREVIEW = new CfVersion(Opcodes.V16 | Opcodes.V_PREVIEW);
private final int version;
@@ -47,7 +54,8 @@
CfVersion.V12,
CfVersion.V13,
CfVersion.V14,
- CfVersion.V15
+ CfVersion.V15,
+ CfVersion.V16
};
// Private constructor in case we want to canonicalize versions.
@@ -64,19 +72,25 @@
}
public int minor() {
- return version >> 16;
+ return version >>> 16;
}
public int raw() {
return version;
}
+ public boolean isPreview() {
+ return minor() == Opcodes.V_PREVIEW >>> 16;
+ }
+
private static void specify(StructuralSpecification<CfVersion, ?> spec) {
spec.withInt(CfVersion::major).withInt(CfVersion::minor);
}
public static Iterable<CfVersion> rangeInclusive(CfVersion from, CfVersion to) {
assert from.isLessThanOrEqualTo(to);
+ assert !from.isPreview() : "This method does not handle preview versions";
+ assert !to.isPreview() : "This method does not handle preview versions";
return Arrays.stream(versions)
.filter(version -> version.isGreaterThanOrEqualTo(from))
.filter(version -> version.isLessThanOrEqualTo(to))
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
index 9b55af9..e6db3e1 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -10,12 +10,12 @@
import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
-import com.android.tools.r8.graph.ResolutionResult.ArrayCloneMethodResult;
-import com.android.tools.r8.graph.ResolutionResult.ClassNotFoundResult;
-import com.android.tools.r8.graph.ResolutionResult.IllegalAccessOrNoSuchMethodResult;
-import com.android.tools.r8.graph.ResolutionResult.IncompatibleClassResult;
-import com.android.tools.r8.graph.ResolutionResult.NoSuchMethodResult;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult.ArrayCloneMethodResult;
+import com.android.tools.r8.graph.MethodResolutionResult.ClassNotFoundResult;
+import com.android.tools.r8.graph.MethodResolutionResult.IllegalAccessOrNoSuchMethodResult;
+import com.android.tools.r8.graph.MethodResolutionResult.IncompatibleClassResult;
+import com.android.tools.r8.graph.MethodResolutionResult.NoSuchMethodResult;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.type.InterfaceCollection;
import com.android.tools.r8.ir.analysis.type.InterfaceCollection.Builder;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
@@ -595,7 +595,7 @@
* <p>This is to overcome the shortcoming of the DEX file format that does not allow to encode the
* kind of a method reference.
*/
- public ResolutionResult unsafeResolveMethodDueToDexFormat(DexMethod method) {
+ public MethodResolutionResult unsafeResolveMethodDueToDexFormat(DexMethod method) {
assert checkIfObsolete();
DexType holder = method.holder;
if (holder.isArrayType()) {
@@ -608,13 +608,13 @@
return resolveMethodOn(definition, method);
}
- public ResolutionResult resolveMethod(DexMethod method, boolean isInterface) {
+ public MethodResolutionResult resolveMethod(DexMethod method, boolean isInterface) {
return isInterface
? resolveMethodOnInterface(method.holder, method)
: resolveMethodOnClass(method, method.holder);
}
- public ResolutionResult resolveMethodOn(DexClass holder, DexMethod method) {
+ public MethodResolutionResult resolveMethodOn(DexClass holder, DexMethod method) {
return holder.isInterface()
? resolveMethodOnInterface(holder, method)
: resolveMethodOnClass(method, holder);
@@ -631,7 +631,8 @@
* @param isInterface Indicates if resolution is to be done according to class or interface.
* @return The result of resolution.
*/
- public ResolutionResult resolveMethodOn(DexType holder, DexMethod method, boolean isInterface) {
+ public MethodResolutionResult resolveMethodOn(
+ DexType holder, DexMethod method, boolean isInterface) {
assert checkIfObsolete();
return isInterface
? resolveMethodOnInterface(holder, method)
@@ -645,7 +646,7 @@
* 10.7 of the Java Language Specification</a>. All invokations will have target java.lang.Object
* except clone which has no target.
*/
- private ResolutionResult resolveMethodOnArray(DexType holder, DexMethod method) {
+ private MethodResolutionResult resolveMethodOnArray(DexType holder, DexMethod method) {
assert checkIfObsolete();
assert holder.isArrayType();
if (method.name == dexItemFactory().cloneMethodName) {
@@ -655,7 +656,7 @@
}
}
- public ResolutionResult resolveMethodOnClass(DexMethod method) {
+ public MethodResolutionResult resolveMethodOnClass(DexMethod method) {
return resolveMethodOnClass(method, method.holder);
}
@@ -670,7 +671,7 @@
* invoke on the given descriptor to a corresponding invoke on the resolved descriptor, as the
* resolved method is used as basis for dispatch.
*/
- public ResolutionResult resolveMethodOnClass(DexMethod method, DexType holder) {
+ public MethodResolutionResult resolveMethodOnClass(DexMethod method, DexType holder) {
assert checkIfObsolete();
if (holder.isArrayType()) {
return resolveMethodOnArray(holder, method);
@@ -686,11 +687,11 @@
return resolveMethodOnClass(method, clazz);
}
- public ResolutionResult resolveMethodOnClass(DexMethod method, DexClass clazz) {
+ public MethodResolutionResult resolveMethodOnClass(DexMethod method, DexClass clazz) {
assert checkIfObsolete();
assert !clazz.isInterface();
// Step 2:
- ResolutionResult result = resolveMethodOnClassStep2(clazz, method, clazz);
+ MethodResolutionResult result = resolveMethodOnClassStep2(clazz, method, clazz);
if (result != null) {
return result;
}
@@ -703,7 +704,7 @@
* href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.3">Section
* 5.4.3.3 of the JVM Spec</a>.
*/
- private ResolutionResult resolveMethodOnClassStep2(
+ private MethodResolutionResult resolveMethodOnClassStep2(
DexClass clazz, DexMethod method, DexClass initialResolutionHolder) {
// Pt. 1: Signature polymorphic method check.
// See also <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.9">
@@ -741,7 +742,7 @@
* 5.4.3.3 of the JVM Spec</a>. As this is the same for interfaces and classes, we share one
* implementation.
*/
- private ResolutionResult resolveMethodStep3(DexClass clazz, DexMethod method) {
+ private MethodResolutionResult resolveMethodStep3(DexClass clazz, DexMethod method) {
MaximallySpecificMethodsBuilder builder = new MaximallySpecificMethodsBuilder();
resolveMethodStep3Helper(method, clazz, builder);
return builder.resolve(clazz);
@@ -807,7 +808,7 @@
return method != null && !method.accessFlags.isPrivate() && !method.accessFlags.isStatic();
}
- public ResolutionResult resolveMethodOnInterface(DexMethod method) {
+ public MethodResolutionResult resolveMethodOnInterface(DexMethod method) {
return resolveMethodOnInterface(method.holder, method);
}
@@ -822,7 +823,7 @@
* invoke on the given descriptor to a corresponding invoke on the resolved descriptor, as the
* resolved method is used as basis for dispatch.
*/
- public ResolutionResult resolveMethodOnInterface(DexType holder, DexMethod desc) {
+ public MethodResolutionResult resolveMethodOnInterface(DexType holder, DexMethod desc) {
assert checkIfObsolete();
if (holder.isArrayType()) {
return IncompatibleClassResult.INSTANCE;
@@ -840,7 +841,7 @@
return resolveMethodOnInterface(definition, desc);
}
- public ResolutionResult resolveMethodOnInterface(DexClass definition, DexMethod desc) {
+ public MethodResolutionResult resolveMethodOnInterface(DexClass definition, DexMethod desc) {
assert checkIfObsolete();
assert definition.isInterface();
// Step 2: Look for exact method on interface.
@@ -1004,12 +1005,12 @@
: null;
}
- ResolutionResult resolve(DexClass initialResolutionHolder) {
+ MethodResolutionResult resolve(DexClass initialResolutionHolder) {
assert initialResolutionHolder != null;
return internalResolve(initialResolutionHolder);
}
- private ResolutionResult internalResolve(DexClass initialResolutionHolder) {
+ private MethodResolutionResult internalResolve(DexClass initialResolutionHolder) {
if (maximallySpecificMethods.isEmpty()) {
return NoSuchMethodResult.INSTANCE;
}
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 b509c1f..ebbcd93 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -28,6 +28,7 @@
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoFactory;
import com.android.tools.r8.ir.optimize.library.LibraryMemberOptimizer;
import com.android.tools.r8.ir.optimize.library.LibraryMethodSideEffectModelCollection;
+import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.shaking.LibraryModeledPredicate;
@@ -84,6 +85,7 @@
private final LibraryMethodSideEffectModelCollection libraryMethodSideEffectModelCollection;
// Optimizations.
+ private final ArgumentPropagator argumentPropagator;
private final CallSiteOptimizationInfoPropagator callSiteOptimizationInfoPropagator;
private final LibraryMemberOptimizer libraryMemberOptimizer;
private final ProtoShrinker protoShrinker;
@@ -121,9 +123,16 @@
this.rewritePrefix = mapper;
if (enableWholeProgramOptimizations() && options().callSiteOptimizationOptions().isEnabled()) {
- this.callSiteOptimizationInfoPropagator =
- new CallSiteOptimizationInfoPropagator(withLiveness());
+ if (options().callSiteOptimizationOptions().isExperimentalArgumentPropagationEnabled()) {
+ this.argumentPropagator = new ArgumentPropagator(withLiveness());
+ this.callSiteOptimizationInfoPropagator = null;
+ } else {
+ this.argumentPropagator = null;
+ this.callSiteOptimizationInfoPropagator =
+ new CallSiteOptimizationInfoPropagator(withLiveness());
+ }
} else {
+ this.argumentPropagator = null;
this.callSiteOptimizationInfoPropagator = null;
}
@@ -323,6 +332,13 @@
return callSiteOptimizationInfoPropagator;
}
+ public <E extends Throwable> void withArgumentPropagator(
+ ThrowingConsumer<ArgumentPropagator, E> consumer) throws E {
+ if (argumentPropagator != null) {
+ consumer.accept(argumentPropagator);
+ }
+ }
+
public <E extends Throwable> void withCallSiteOptimizationInfoPropagator(
ThrowingConsumer<CallSiteOptimizationInfoPropagator, E> consumer) throws E {
if (callSiteOptimizationInfoPropagator != null) {
@@ -480,6 +496,10 @@
this.mainDexRootSet = mainDexRootSet;
}
+ public boolean hasMainDexRootSet() {
+ return mainDexRootSet != null;
+ }
+
public MainDexRootSet getMainDexRootSet() {
return mainDexRootSet;
}
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 49cab56..0213b0e 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -730,6 +730,33 @@
thisLocalInfo.index, debugLocalInfo, thisLocalInfo.start, thisLocalInfo.end));
}
+ @Override
+ public Code getCodeAsInlining(DexMethod caller, DexMethod callee) {
+ Position callerPosition = Position.synthetic(0, caller, null);
+ List<CfInstruction> newInstructions = new ArrayList<>(instructions.size() + 2);
+ CfLabel firstLabel;
+ if (instructions.get(0).isLabel()) {
+ firstLabel = instructions.get(0).asLabel();
+ } else {
+ firstLabel = new CfLabel();
+ newInstructions.add(firstLabel);
+ }
+ newInstructions.add(new CfPosition(firstLabel, callerPosition));
+ for (CfInstruction instruction : instructions) {
+ if (instruction.isPosition()) {
+ CfPosition oldPosition = instruction.asPosition();
+ newInstructions.add(
+ new CfPosition(
+ oldPosition.getLabel(),
+ oldPosition.getPosition().withOutermostCallerPosition(callerPosition)));
+ } else {
+ newInstructions.add(instruction);
+ }
+ }
+ return new CfCode(
+ originalHolder, maxStack, maxLocals, newInstructions, tryCatchRanges, localVariables);
+ }
+
public StackMapStatus verifyFrames(DexEncodedMethod method, AppView<?> appView, Origin origin) {
return verifyFrames(method, appView, origin, RewrittenPrototypeDescription.none());
}
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index 0f6438f..83fc48c 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -103,4 +103,8 @@
public boolean verifyNoInputReaders() {
return true;
}
+
+ public Code getCodeAsInlining(DexMethod caller, DexMethod callee) {
+ throw new Unreachable();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/Definition.java b/src/main/java/com/android/tools/r8/graph/Definition.java
index b3dcc3a..00bca6f 100644
--- a/src/main/java/com/android/tools/r8/graph/Definition.java
+++ b/src/main/java/com/android/tools/r8/graph/Definition.java
@@ -26,6 +26,8 @@
AccessFlags<?> getAccessFlags();
+ DexClass getContextClass();
+
DexType getContextType();
DexDefinition getDefinition();
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 f3937c2..e4d00aa 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -742,6 +742,11 @@
}
@Override
+ public DexClass getContextClass() {
+ return this;
+ }
+
+ @Override
public DexClass getDefinition() {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
index f82bcbc..703e841 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
@@ -27,6 +27,11 @@
}
@Override
+ public DexClass getContextClass() {
+ return getHolder();
+ }
+
+ @Override
public DexType getContextType() {
return getHolderType();
}
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 378640d..589ae3a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
+import com.android.tools.r8.graph.DexDebugEvent.SetInlineFrame;
import com.android.tools.r8.graph.DexDebugEvent.StartLocal;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.NumberGenerator;
@@ -166,6 +167,52 @@
return new DexDebugInfo(debugInfo.startLine, newParameters, debugInfo.events);
}
+ @Override
+ public Code getCodeAsInlining(DexMethod caller, DexMethod callee) {
+ return new DexCode(
+ registerSize,
+ incomingRegisterSize,
+ outgoingRegisterSize,
+ instructions,
+ tries,
+ handlers,
+ debugInfoAsInlining(caller, callee));
+ }
+
+ private DexDebugInfo debugInfoAsInlining(DexMethod caller, DexMethod callee) {
+ Position callerPosition = Position.synthetic(0, caller, null);
+ if (debugInfo == null) {
+ // If the method has no debug info we generate a preamble position to denote the inlining.
+ // This is consistent with the building IR for inlining which will always ensure the method
+ // has a position.
+ return new DexDebugInfo(
+ 0,
+ new DexString[callee.getArity()],
+ new DexDebugEvent[] {
+ new DexDebugEvent.SetInlineFrame(callee, callerPosition),
+ DexDebugEvent.ZERO_CHANGE_DEFAULT_EVENT
+ });
+ }
+ DexDebugEvent[] oldEvents = debugInfo.events;
+ DexDebugEvent[] newEvents = new DexDebugEvent[oldEvents.length + 1];
+ int i = 0;
+ newEvents[i++] = new DexDebugEvent.SetInlineFrame(callee, callerPosition);
+ for (DexDebugEvent event : oldEvents) {
+ if (event instanceof SetInlineFrame) {
+ SetInlineFrame oldFrame = (SetInlineFrame) event;
+ newEvents[i++] =
+ new SetInlineFrame(
+ oldFrame.callee,
+ oldFrame.caller == null
+ ? callerPosition
+ : oldFrame.caller.withOutermostCallerPosition(callerPosition));
+ } else {
+ newEvents[i++] = event;
+ }
+ }
+ return new DexDebugInfo(debugInfo.startLine, debugInfo.parameters, newEvents);
+ }
+
public static int getLargestPrefix(DexItemFactory factory, DexString name) {
if (name != null && name.endsWith(factory.thisName)) {
String string = name.toString();
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 b213660..1bbd88a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
@@ -23,6 +23,8 @@
public static final DexDebugEvent[] EMPTY_ARRAY = {};
+ public static final DexDebugEvent.Default ZERO_CHANGE_DEFAULT_EVENT = Default.create(0, 0);
+
public void collectIndexedItems(IndexedItemCollection collection, GraphLens graphLens) {
// Empty by default.
}
@@ -583,6 +585,16 @@
this.value = value;
}
+ public static int computeSpecialOpcode(int lineDelta, int pcDelta) {
+ return Constants.DBG_FIRST_SPECIAL
+ + (lineDelta - Constants.DBG_LINE_BASE)
+ + Constants.DBG_LINE_RANGE * pcDelta;
+ }
+
+ public static Default create(int lineDelta, int pcDelta) {
+ return new Default(computeSpecialOpcode(lineDelta, pcDelta));
+ }
+
@Override
public void writeOn(
DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
index bdfe991..d7c008c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexDebugEvent.Default;
import com.android.tools.r8.graph.DexDebugEvent.StartLocal;
import com.android.tools.r8.ir.code.Argument;
import com.android.tools.r8.ir.code.DebugLocalsChange;
@@ -253,23 +254,17 @@
// TODO(herhut): To be super clever, encode only the part that is above limit.
lineDelta = 0;
}
- int specialOpcode = computeSpecialOpcode(lineDelta, pcDelta);
+ int specialOpcode = Default.computeSpecialOpcode(lineDelta, pcDelta);
if (specialOpcode > Constants.DBG_LAST_SPECIAL) {
events.add(factory.createAdvancePC(pcDelta));
// TODO(herhut): To be super clever, encode only the part that is above limit.
- specialOpcode = computeSpecialOpcode(lineDelta, 0);
+ specialOpcode = Default.computeSpecialOpcode(lineDelta, 0);
}
assert specialOpcode >= Constants.DBG_FIRST_SPECIAL;
assert specialOpcode <= Constants.DBG_LAST_SPECIAL;
events.add(factory.createDefault(specialOpcode));
}
- private static int computeSpecialOpcode(int lineDelta, int pcDelta) {
- return Constants.DBG_FIRST_SPECIAL
- + (lineDelta - Constants.DBG_LINE_BASE)
- + Constants.DBG_LINE_RANGE * pcDelta;
- }
-
private static void emitLocalChangeEvents(
Int2ReferenceMap<DebugLocalInfo> previousLocals,
Int2ReferenceMap<DebugLocalInfo> nextLocals,
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 c6913f8..cb44ff9 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -199,13 +199,7 @@
}
public DexType getArgumentType(int argumentIndex) {
- if (isStatic()) {
- return getReference().getParameter(argumentIndex);
- }
- if (argumentIndex == 0) {
- return getHolderType();
- }
- return getReference().getParameter(argumentIndex - 1);
+ return getReference().getArgumentType(argumentIndex, isStatic());
}
public int getNumberOfArguments() {
@@ -993,9 +987,15 @@
assert !accessFlags.isFinal();
// static abstract is an invalid access combination and we should never create that.
assert !accessFlags.isStatic();
- accessFlags.setAbstract();
- this.code = null;
- return this;
+ return builder(this)
+ .modifyAccessFlags(MethodAccessFlags::setAbstract)
+ .setIsLibraryMethodOverrideIf(
+ isNonPrivateVirtualMethod() && !isLibraryMethodOverride().isUnknown(),
+ isLibraryMethodOverride())
+ .unsetCode()
+ .addBuildConsumer(
+ method -> OptimizationFeedbackSimple.getInstance().unsetBridgeInfo(method))
+ .build();
}
/**
@@ -1585,7 +1585,7 @@
new ProgramMethod(holder, newMethod), simpleInliningConstraint));
}
- private Builder addBuildConsumer(Consumer<DexEncodedMethod> consumer) {
+ public Builder addBuildConsumer(Consumer<DexEncodedMethod> consumer) {
this.buildConsumer = this.buildConsumer.andThen(consumer);
return this;
}
@@ -1721,6 +1721,10 @@
return this;
}
+ public Builder unsetCode() {
+ return setCode(null);
+ }
+
public DexEncodedMethod build() {
assert method != null;
assert accessFlags != null;
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index e8f5829..0100fd5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexDebugEvent.AdvanceLine;
import com.android.tools.r8.graph.DexDebugEvent.AdvancePC;
import com.android.tools.r8.graph.DexDebugEvent.Default;
@@ -717,6 +718,44 @@
.put(doubleType, boxedDoubleType)
.build());
+ public final Map<DexType, DexMethod> unboxPrimitiveMethod =
+ ImmutableMap.<DexType, DexMethod>builder()
+ .put(boxedBooleanType, createUnboxMethod(booleanType, unboxBooleanMethodName))
+ .put(boxedByteType, createUnboxMethod(byteType, unboxByteMethodName))
+ .put(boxedCharType, createUnboxMethod(charType, unboxCharMethodName))
+ .put(boxedShortType, createUnboxMethod(shortType, unboxShortMethodName))
+ .put(boxedIntType, createUnboxMethod(intType, unboxIntMethodName))
+ .put(boxedLongType, createUnboxMethod(longType, unboxLongMethodName))
+ .put(boxedFloatType, createUnboxMethod(floatType, unboxFloatMethodName))
+ .put(boxedDoubleType, createUnboxMethod(doubleType, unboxDoubleMethodName))
+ .build();
+
+ private DexMethod createUnboxMethod(DexType primitiveType, DexString unboxMethodName) {
+ DexProto proto = createProto(primitiveType);
+ return createMethod(primitiveToBoxed.get(primitiveType), proto, unboxMethodName);
+ }
+
+ // Works both with the boxed and unboxed type.
+ public DexMethod getUnboxPrimitiveMethod(DexType type) {
+ DexType boxType = primitiveToBoxed.getOrDefault(type, type);
+ DexMethod unboxMethod = unboxPrimitiveMethod.get(boxType);
+ if (unboxMethod == null) {
+ throw new Unreachable("Invalid primitive type descriptor: " + type);
+ }
+ return unboxMethod;
+ }
+
+ // Works both with the boxed and unboxed type.
+ public DexMethod getBoxPrimitiveMethod(DexType type) {
+ DexType boxType = primitiveToBoxed.getOrDefault(type, type);
+ DexType primitive = getPrimitiveFromBoxed(boxType);
+ if (primitive == null) {
+ throw new Unreachable("Invalid primitive type descriptor: " + type);
+ }
+ DexProto proto = createProto(boxType, primitive);
+ return createMethod(boxType, proto, valueOfMethodName);
+ }
+
public DexType getBoxedForPrimitiveType(DexType primitive) {
assert primitive.isPrimitiveType();
return primitiveToBoxed.get(primitive);
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 15ac70a..5a109e7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -59,6 +59,16 @@
return visitor.visitDexMethod(this, other);
}
+ public DexType getArgumentType(int argumentIndex, boolean isStatic) {
+ if (isStatic) {
+ return getParameter(argumentIndex);
+ }
+ if (argumentIndex == 0) {
+ return getHolderType();
+ }
+ return getParameter(argumentIndex - 1);
+ }
+
public DexType getParameter(int index) {
return proto.getParameter(index);
}
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 96c3084..d276e9b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -7,6 +7,8 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -52,6 +54,10 @@
return Reference.classFromDescriptor(toDescriptorString());
}
+ public TypeElement toTypeElement(AppView<?> appView) {
+ return TypeElement.fromDexType(this, Nullability.maybeNull(), appView);
+ }
+
@Override
public int compareTo(DexReference other) {
if (other.isDexType()) {
diff --git a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
index 554cadc..62b62b9 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
@@ -17,6 +17,16 @@
return UnknownFieldResolutionResult.INSTANCE;
}
+ @Override
+ public boolean isFieldResolutionResult() {
+ return true;
+ }
+
+ @Override
+ public FieldResolutionResult asFieldResolutionResult() {
+ return this;
+ }
+
public DexEncodedField getResolvedField() {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
index d8c3142..8fa722d 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.shaking.KeepClassInfo;
import com.android.tools.r8.shaking.KeepFieldInfo;
import com.android.tools.r8.shaking.KeepMethodInfo;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
import java.util.List;
import java.util.function.Consumer;
@@ -84,6 +85,7 @@
private final AppView<?> appView;
private final Mode mode;
+ private final InternalOptions options;
private final GenericSignatureContextBuilder contextBuilder;
private GenericSignatureCorrectnessHelper(
@@ -91,6 +93,7 @@
this.appView = appView;
this.contextBuilder = contextBuilder;
this.mode = mode;
+ this.options = appView.options();
}
public static GenericSignatureCorrectnessHelper createForInitialCheck(
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 8e436ba..8da7519 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.optimize.MemberRebindingLens;
import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.utils.Action;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.SetUtils;
@@ -505,11 +506,12 @@
return true;
}
- public <T extends DexReference> boolean assertPinnedNotModified(KeepInfoCollection keepInfo) {
+ public <T extends DexReference> boolean assertPinnedNotModified(
+ KeepInfoCollection keepInfo, InternalOptions options) {
List<DexReference> pinnedItems = new ArrayList<>();
- keepInfo.forEachPinnedType(pinnedItems::add);
- keepInfo.forEachPinnedMethod(pinnedItems::add);
- keepInfo.forEachPinnedField(pinnedItems::add);
+ keepInfo.forEachPinnedType(pinnedItems::add, options);
+ keepInfo.forEachPinnedMethod(pinnedItems::add, options);
+ keepInfo.forEachPinnedField(pinnedItems::add, options);
return assertReferencesNotModified(pinnedItems);
}
diff --git a/src/main/java/com/android/tools/r8/graph/ImmediateProgramSubtypingInfo.java b/src/main/java/com/android/tools/r8/graph/ImmediateProgramSubtypingInfo.java
new file mode 100644
index 0000000..ac08e3f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ImmediateProgramSubtypingInfo.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+import static com.google.common.base.Predicates.alwaysTrue;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.BiPredicate;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+public class ImmediateProgramSubtypingInfo {
+
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
+ private final Map<DexProgramClass, List<DexProgramClass>> immediateSubtypes;
+
+ private ImmediateProgramSubtypingInfo(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ Map<DexProgramClass, List<DexProgramClass>> immediateSubtypes) {
+ this.appView = appView;
+ this.immediateSubtypes = immediateSubtypes;
+ }
+
+ public static ImmediateProgramSubtypingInfo create(
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
+ Map<DexProgramClass, List<DexProgramClass>> immediateSubtypes = new IdentityHashMap<>();
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ clazz.forEachImmediateSupertype(
+ supertype -> {
+ DexProgramClass superclass = asProgramClassOrNull(appView.definitionFor(supertype));
+ if (superclass != null) {
+ immediateSubtypes.computeIfAbsent(superclass, ignoreKey(ArrayList::new)).add(clazz);
+ }
+ });
+ }
+ return new ImmediateProgramSubtypingInfo(appView, immediateSubtypes);
+ }
+
+ public void forEachImmediateSuperClass(
+ DexProgramClass clazz, BiConsumer<? super DexType, ? super DexClass> consumer) {
+ forEachImmediateSuperClassMatching(clazz, (supertype, superclass) -> true, consumer);
+ }
+
+ public void forEachImmediateSuperClassMatching(
+ DexProgramClass clazz,
+ BiPredicate<? super DexType, ? super DexClass> predicate,
+ BiConsumer<? super DexType, ? super DexClass> consumer) {
+ clazz.forEachImmediateSupertype(
+ supertype -> {
+ DexClass superclass = appView.definitionFor(supertype);
+ if (predicate.test(supertype, superclass)) {
+ consumer.accept(supertype, superclass);
+ }
+ });
+ }
+
+ public void forEachImmediateSubClass(
+ DexProgramClass clazz, Consumer<? super DexProgramClass> consumer) {
+ forEachImmediateSubClassMatching(clazz, alwaysTrue(), consumer);
+ }
+
+ public void forEachImmediateSubClassMatching(
+ DexProgramClass clazz,
+ Predicate<? super DexProgramClass> predicate,
+ Consumer<? super DexProgramClass> consumer) {
+ getSubclasses(clazz)
+ .forEach(
+ subclass -> {
+ if (predicate.test(subclass)) {
+ consumer.accept(subclass);
+ }
+ });
+ }
+
+ public List<DexProgramClass> getSubclasses(DexProgramClass clazz) {
+ return immediateSubtypes.getOrDefault(clazz, Collections.emptyList());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index fbefa45..a520ae3 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -150,6 +150,11 @@
return code;
}
+ @Override
+ public Code getCodeAsInlining(DexMethod caller, DexMethod callee) {
+ return asCfCode().getCodeAsInlining(caller, callee);
+ }
+
public static class DebugParsingOptions {
public final boolean lineInfo;
public final boolean localInfo;
diff --git a/src/main/java/com/android/tools/r8/graph/MemberResolutionResult.java b/src/main/java/com/android/tools/r8/graph/MemberResolutionResult.java
index 285859d..c3f3c42 100644
--- a/src/main/java/com/android/tools/r8/graph/MemberResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/MemberResolutionResult.java
@@ -24,4 +24,20 @@
ProgramDefinition context, AppView<? extends AppInfoWithClassHierarchy> appView) {
return isAccessibleFrom(context, appView.appInfo());
}
+
+ public boolean isFieldResolutionResult() {
+ return false;
+ }
+
+ public boolean isMethodResolutionResult() {
+ return false;
+ }
+
+ public FieldResolutionResult asFieldResolutionResult() {
+ return null;
+ }
+
+ public MethodResolutionResult asMethodResolutionResult() {
+ return null;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
similarity index 98%
rename from src/main/java/com/android/tools/r8/graph/ResolutionResult.java
rename to src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
index e3558a2..aadd446 100644
--- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
@@ -19,7 +19,18 @@
import java.util.function.BiPredicate;
import java.util.function.Consumer;
-public abstract class ResolutionResult extends MemberResolutionResult<DexEncodedMethod, DexMethod> {
+public abstract class MethodResolutionResult
+ extends MemberResolutionResult<DexEncodedMethod, DexMethod> {
+
+ @Override
+ public boolean isMethodResolutionResult() {
+ return true;
+ }
+
+ @Override
+ public MethodResolutionResult asMethodResolutionResult() {
+ return this;
+ }
/**
* Returns true if resolution succeeded *and* the resolved method has a known definition.
@@ -137,7 +148,7 @@
LambdaDescriptor lambdaInstance, AppInfoWithClassHierarchy appInfo);
/** Result for a resolution that succeeds with a known declaration/definition. */
- public static class SingleResolutionResult extends ResolutionResult
+ public static class SingleResolutionResult extends MethodResolutionResult
implements SuccessfulMemberResolutionResult<DexEncodedMethod, DexMethod> {
private final DexClass initialResolutionHolder;
private final DexClass resolvedHolder;
@@ -700,7 +711,7 @@
}
}
- abstract static class EmptyResult extends ResolutionResult {
+ abstract static class EmptyResult extends MethodResolutionResult {
@Override
public final DexClassAndMethod lookupInvokeSpecialTarget(
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
index 946bdbc..7f09192 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
@@ -39,6 +39,9 @@
/** Instantiated classes without contexts. */
final Set<DexProgramClass> classesWithoutAllocationSiteTracking = Sets.newIdentityHashSet();
+ /** Set of annotation types for which the subtype hierarchy is unknown from that type. */
+ final Set<DexProgramClass> annotationsWithUnknownSubtypeHierarchy = Sets.newIdentityHashSet();
+
/**
* Set of interface types for which the subtype hierarchy is unknown from that type.
*
@@ -103,7 +106,8 @@
if (!clazz.isInterface()) {
return false;
}
- return interfacesWithUnknownSubtypeHierarchy.contains(clazz)
+ return annotationsWithUnknownSubtypeHierarchy.contains(clazz)
+ || interfacesWithUnknownSubtypeHierarchy.contains(clazz)
|| isImmediateInterfaceOfInstantiatedLambda(clazz);
}
@@ -227,6 +231,8 @@
.removeIf(entry -> removedClasses.contains(entry.getKey().getType()));
classesWithoutAllocationSiteTracking.removeIf(
clazz -> removedClasses.contains(clazz.getType()));
+ annotationsWithUnknownSubtypeHierarchy.removeIf(
+ annotation -> removedClasses.contains(annotation.getType()));
boolean removed =
interfacesWithUnknownSubtypeHierarchy.removeIf(
iface -> removedClasses.contains(iface.getType()));
@@ -242,6 +248,9 @@
for (DexProgramClass clazz : classesWithoutAllocationSiteTracking) {
assert liveTypes.contains(clazz.getType());
}
+ for (DexProgramClass annotation : annotationsWithUnknownSubtypeHierarchy) {
+ assert liveTypes.contains(annotation.getType());
+ }
for (DexProgramClass iface : interfacesWithUnknownSubtypeHierarchy) {
assert liveTypes.contains(iface.getType());
}
@@ -338,6 +347,16 @@
return false;
}
+ public boolean recordInstantiatedAnnotation(DexProgramClass annotation, AppInfo appInfo) {
+ assert annotation.isInterface();
+ assert annotation.isAnnotation();
+ if (annotationsWithUnknownSubtypeHierarchy.add(annotation)) {
+ populateInstantiatedHierarchy(appInfo, annotation);
+ return true;
+ }
+ return false;
+ }
+
public boolean recordInstantiatedInterface(DexProgramClass iface, AppInfo appInfo) {
assert iface.isInterface();
assert !iface.isAnnotation();
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java b/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
index b36e87c..ab0ee34 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
@@ -42,6 +42,7 @@
getDefinition().rewriteAllAnnotations(rewriter);
}
+ @Override
DexProgramClass getContextClass();
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramField.java b/src/main/java/com/android/tools/r8/graph/ProgramField.java
index d53cb1c..0007521 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramField.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramField.java
@@ -33,6 +33,11 @@
}
@Override
+ public DexProgramClass getContextClass() {
+ return getHolder();
+ }
+
+ @Override
public boolean isProgramField() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
index a34b49d..5d5a755 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -68,6 +68,11 @@
}
@Override
+ public DexProgramClass getContextClass() {
+ return getHolder();
+ }
+
+ @Override
public boolean isProgramMember() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/graph/TreeFixerBase.java b/src/main/java/com/android/tools/r8/graph/TreeFixerBase.java
index 969ac7d..d09c3f5 100644
--- a/src/main/java/com/android/tools/r8/graph/TreeFixerBase.java
+++ b/src/main/java/com/android/tools/r8/graph/TreeFixerBase.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.DescriptorUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.IdentityHashMap;
@@ -202,13 +203,30 @@
DexType newInnerClassType = fixupTypeOrNull(innerClassType);
DexType outerClassType = innerClassAttribute.getOuter();
DexType newOuterClassType = fixupTypeOrNull(outerClassType);
+ DexString newInnerName = innerClassAttribute.getInnerName();
+ // Compute the new inner name if the attribute changed. This could end up 'fixing' invalid
+ // inner class attributes.
+ boolean innerClassAttributeChanged =
+ newInnerClassType != innerClassType || newOuterClassType != outerClassType;
+ if (innerClassAttributeChanged && innerClassType != null && outerClassType != null) {
+ String innerClassName =
+ DescriptorUtils.getInnerClassName(
+ newOuterClassType.toDescriptorString(), newInnerClassType.toDescriptorString());
+ if (innerClassName != null) {
+ newInnerName = dexItemFactory.createString(innerClassName);
+ } else {
+ // If run without treeshaking and the outer type is missing we are not pruning the
+ // relationship.
+ // TODO(b/196503304): Enable the below asserts when resolved.
+ assert !appView.options().isTreeShakingEnabled() || true;
+ // assert appView.appInfo().definitionForWithoutExistenceAssert(newOuterClassType) ==
+ // null;
+ }
+ }
newInnerClassAttributes.add(
new InnerClassAttribute(
- innerClassAttribute.getAccess(),
- newInnerClassType,
- newOuterClassType,
- innerClassAttribute.getInnerName()));
- changed |= newInnerClassType != innerClassType || newOuterClassType != outerClassType;
+ innerClassAttribute.getAccess(), newInnerClassType, newOuterClassType, newInnerName));
+ changed |= innerClassAttributeChanged;
}
return changed ? newInnerClassAttributes : innerClassAttributes;
}
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java
deleted file mode 100644
index 28d6be7..0000000
--- a/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java
+++ /dev/null
@@ -1,69 +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.graph.analysis;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClasspathClass;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.ProgramDefinition;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter.Mode;
-import com.android.tools.r8.utils.collections.ProgramMethodSet;
-import java.util.function.Consumer;
-
-public class DesugaredLibraryConversionWrapperAnalysis extends EnqueuerAnalysis
- implements EnqueuerInvokeAnalysis {
-
- private final AppView<?> appView;
- private final DesugaredLibraryAPIConverter converter;
-
- public DesugaredLibraryConversionWrapperAnalysis(AppView<?> appView) {
- this.appView = appView;
- this.converter =
- new DesugaredLibraryAPIConverter(appView, Mode.GENERATE_CALLBACKS_AND_WRAPPERS);
- }
-
- @Override
- public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) {
- converter.registerCallbackIfRequired(method);
- }
-
- private void traceInvoke(DexMethod invokedMethod) {
- converter.registerWrappersForLibraryInvokeIfRequired(invokedMethod);
- }
-
- @Override
- public void traceInvokeStatic(DexMethod invokedMethod, ProgramMethod context) {
- this.traceInvoke(invokedMethod);
- }
-
- @Override
- public void traceInvokeDirect(DexMethod invokedMethod, ProgramMethod context) {
- this.traceInvoke(invokedMethod);
- }
-
- @Override
- public void traceInvokeInterface(DexMethod invokedMethod, ProgramMethod context) {
- this.traceInvoke(invokedMethod);
- }
-
- @Override
- public void traceInvokeSuper(DexMethod invokedMethod, ProgramMethod context) {
- this.traceInvoke(invokedMethod);
- }
-
- @Override
- public void traceInvokeVirtual(DexMethod invokedMethod, ProgramMethod context) {
- this.traceInvoke(invokedMethod);
- }
-
- public ProgramMethodSet generateCallbackMethods() {
- return converter.generateCallbackMethods();
- }
-
- public void generateWrappers(Consumer<DexClasspathClass> synthesizedCallback) {
- converter.synthesizeWrappers(synthesizedCallback);
- }
-}
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 d5f2b8b..87a3211 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -17,9 +17,9 @@
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.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.horizontalclassmerging.code.VirtualMethodEntryPointSynthesizedCode;
import com.android.tools.r8.ir.synthetic.AbstractSynthesizedCode;
import com.android.tools.r8.utils.ListUtils;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKeepRules.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKeepRules.java
index 82b76d9..63461b5 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKeepRules.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKeepRules.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
import com.android.tools.r8.shaking.KeepInfoCollection;
+import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.util.Set;
@@ -19,20 +20,22 @@
private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final KeepInfoCollection keepInfo;
+ private final InternalOptions options;
private final Set<DexType> dontMergeTypes = Sets.newIdentityHashSet();
public NoKeepRules(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
this.keepInfo = appView.getKeepInfo();
+ this.options = appView.options();
appView.appInfo().classes().forEach(this::processClass);
}
private void processClass(DexProgramClass clazz) {
DexType type = clazz.getType();
- boolean pinHolder = keepInfo.getClassInfo(clazz).isPinned();
+ boolean pinHolder = keepInfo.getClassInfo(clazz).isPinned(options);
for (DexEncodedMember<?, ?> member : clazz.members()) {
- if (keepInfo.getMemberInfo(member, clazz).isPinned()) {
+ if (keepInfo.getMemberInfo(member, clazz).isPinned(options)) {
pinHolder = true;
Iterables.addAll(
dontMergeTypes,
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java
index 79b2bb2..54868a8 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java
@@ -11,8 +11,8 @@
import com.android.tools.r8.graph.DexMethodSignature;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.MergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventClassMethodAndDefaultMethodCollisions.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventClassMethodAndDefaultMethodCollisions.java
index 9ec219c..7c53bc3 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventClassMethodAndDefaultMethodCollisions.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventClassMethodAndDefaultMethodCollisions.java
@@ -12,7 +12,7 @@
import com.android.tools.r8.graph.DexMethodSignature;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.horizontalclassmerging.MergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
import com.android.tools.r8.horizontalclassmerging.SubtypingForrestForClasses;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
index dbaf662..a46b2e0 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
@@ -13,8 +13,8 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.CatchHandlers.CatchHandler;
@@ -332,7 +332,7 @@
}
}
DexMethod method = instruction.getInvokedMethod();
- ResolutionResult resolutionResult =
+ MethodResolutionResult resolutionResult =
appView.appInfo().resolveMethodOnInterface(method.holder, method);
if (!resolutionResult.isSingleResolution()) {
return false;
@@ -393,7 +393,7 @@
if (superType == null) {
return false;
}
- ResolutionResult resolutionResult =
+ MethodResolutionResult resolutionResult =
appView.appInfo().resolveMethodOn(superType, method, instruction.isInterface);
if (!resolutionResult.isSingleResolution()) {
return false;
@@ -430,7 +430,7 @@
}
}
DexMethod method = instruction.getInvokedMethod();
- ResolutionResult resolutionResult =
+ MethodResolutionResult resolutionResult =
appView.appInfo().resolveMethodOnClass(method, method.holder);
if (!resolutionResult.isSingleResolution()) {
return false;
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 d870dc7..9af0407 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
@@ -32,6 +32,7 @@
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.WorkList;
import com.google.common.collect.Sets;
@@ -81,11 +82,13 @@
private final AppView<?> appView;
private final ProgramMethod context;
private final DexItemFactory dexItemFactory;
+ private final InternalOptions options;
public ValueMayDependOnEnvironmentAnalysis(AppView<?> appView, IRCode code) {
this.appView = appView;
this.context = code.context();
this.dexItemFactory = appView.dexItemFactory();
+ this.options = appView.options();
}
public boolean anyValueMayDependOnEnvironment(Iterable<Value> values) {
@@ -270,7 +273,9 @@
private boolean isNonPinnedClassConstant(Value value) {
Value root = value.getAliasedValue();
return root.isDefinedByInstructionSatisfying(Instruction::isConstClass)
- && !appView.getKeepInfo().isPinned(root.getDefinition().asConstClass().getType(), appView);
+ && !appView
+ .getKeepInfo()
+ .isPinned(root.getDefinition().asConstClass().getType(), appView, options);
}
private boolean addLogicalBinopValueToValueGraph(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
index d1b411a..70c8519 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -191,7 +191,7 @@
abstractValue.join(
argumentAbstractValue,
appView.abstractValueFactory(),
- field.getType(),
+ field.getType().isReferenceType(),
isClassIdField);
assert !abstractValue.isBottom();
} else if (initializationInfo.isSingleValue()) {
@@ -200,7 +200,7 @@
abstractValue.join(
singleValueInitializationInfo,
appView.abstractValueFactory(),
- field.getType(),
+ field.getType().isReferenceType(),
isClassIdField);
} else if (initializationInfo.isTypeInitializationInfo()) {
// TODO(b/149732532): Not handled, for now.
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
index be5a107..01c9fe0 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
@@ -30,6 +30,7 @@
import com.android.tools.r8.shaking.Enqueuer.Mode;
import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.shaking.TreePrunerConfiguration;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import com.google.common.collect.Sets;
@@ -75,6 +76,7 @@
public class GeneratedExtensionRegistryShrinker {
private final AppView<AppInfoWithLiveness> appView;
+ private final InternalOptions options;
private final ProtoReferences references;
private final Map<DexType, Map<DexField, Mode>> removedExtensionFields = new IdentityHashMap<>();
@@ -83,6 +85,7 @@
AppView<AppInfoWithLiveness> appView, ProtoReferences references) {
assert appView.options().protoShrinking().enableGeneratedExtensionRegistryShrinking;
this.appView = appView;
+ this.options = appView.options();
this.references = references;
}
@@ -244,7 +247,7 @@
ProgramField field,
FieldAccessInfoCollection<?> fieldAccessInfoCollection,
KeepInfoCollection keepInfo) {
- if (keepInfo.getFieldInfo(field).isPinned()) {
+ if (keepInfo.getFieldInfo(field).isPinned(options)) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicType.java b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicType.java
new file mode 100644
index 0000000..12d1c4c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicType.java
@@ -0,0 +1,83 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.type;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
+/**
+ * Represents the runtime type of a reference value. This type may be more precise than the value's
+ * statically declared type.
+ *
+ * <p>If a lower bound is known on the runtime type (e.g., {@code new A()}), then {@link
+ * DynamicTypeWithLowerBound} is used.
+ */
+public class DynamicType {
+
+ private static final DynamicType BOTTOM = new DynamicType(TypeElement.getBottom());
+ private static final DynamicType UNKNOWN = new DynamicType(TypeElement.getTop());
+
+ private final TypeElement dynamicUpperBoundType;
+
+ DynamicType(TypeElement dynamicUpperBoundType) {
+ assert dynamicUpperBoundType != null;
+ this.dynamicUpperBoundType = dynamicUpperBoundType;
+ }
+
+ public static DynamicType create(Value value, AppView<AppInfoWithLiveness> appView) {
+ assert value.getType().isReferenceType();
+ TypeElement dynamicUpperBoundType = value.getDynamicUpperBoundType(appView);
+ ClassTypeElement dynamicLowerBoundType = value.getDynamicLowerBoundType(appView);
+ if (dynamicLowerBoundType != null) {
+ assert dynamicUpperBoundType.isClassType();
+ return DynamicTypeWithLowerBound.create(
+ appView, dynamicUpperBoundType.asClassType(), dynamicLowerBoundType);
+ }
+ return new DynamicType(dynamicUpperBoundType);
+ }
+
+ public static DynamicType bottom() {
+ return BOTTOM;
+ }
+
+ public static DynamicType unknown() {
+ return UNKNOWN;
+ }
+
+ public TypeElement getDynamicUpperBoundType() {
+ return dynamicUpperBoundType;
+ }
+
+ public boolean hasDynamicLowerBoundType() {
+ return false;
+ }
+
+ public ClassTypeElement getDynamicLowerBoundType() {
+ return null;
+ }
+
+ public boolean isTrivial(TypeElement staticType) {
+ return staticType == getDynamicUpperBoundType() || isUnknown();
+ }
+
+ public boolean isUnknown() {
+ return getDynamicUpperBoundType().isTop();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+ DynamicType assumption = (DynamicType) other;
+ return dynamicUpperBoundType == assumption.dynamicUpperBoundType;
+ }
+
+ @Override
+ public int hashCode() {
+ return dynamicUpperBoundType.hashCode();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithLowerBound.java b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithLowerBound.java
new file mode 100644
index 0000000..ae61ee3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicTypeWithLowerBound.java
@@ -0,0 +1,60 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.type;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.Objects;
+
+public class DynamicTypeWithLowerBound extends DynamicType {
+
+ private final ClassTypeElement dynamicLowerBoundType;
+
+ DynamicTypeWithLowerBound(
+ ClassTypeElement dynamicUpperBoundType, ClassTypeElement dynamicLowerBoundType) {
+ super(dynamicUpperBoundType);
+ this.dynamicLowerBoundType = dynamicLowerBoundType;
+ }
+
+ public static DynamicTypeWithLowerBound create(
+ AppView<AppInfoWithLiveness> appView,
+ ClassTypeElement dynamicUpperBoundType,
+ ClassTypeElement dynamicLowerBoundType) {
+ assert appView
+ .appInfo()
+ .isSubtype(dynamicLowerBoundType.getClassType(), dynamicUpperBoundType.getClassType());
+ return new DynamicTypeWithLowerBound(dynamicUpperBoundType, dynamicLowerBoundType);
+ }
+
+ @Override
+ public boolean hasDynamicLowerBoundType() {
+ return true;
+ }
+
+ @Override
+ public ClassTypeElement getDynamicLowerBoundType() {
+ return dynamicLowerBoundType;
+ }
+
+ @Override
+ public boolean isTrivial(TypeElement staticType) {
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+ DynamicTypeWithLowerBound assumption = (DynamicTypeWithLowerBound) other;
+ return getDynamicUpperBoundType() == assumption.getDynamicUpperBoundType()
+ && getDynamicLowerBoundType() == assumption.getDynamicLowerBoundType();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getDynamicUpperBoundType(), getDynamicLowerBoundType());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
index a512bd4..750169d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
@@ -11,6 +11,14 @@
public abstract class AbstractValue {
+ public static BottomValue bottom() {
+ return BottomValue.getInstance();
+ }
+
+ public static UnknownValue unknown() {
+ return UnknownValue.getInstance();
+ }
+
public abstract boolean isNonTrivial();
public boolean isSingleBoolean() {
@@ -142,13 +150,15 @@
}
public AbstractValue join(AbstractValue other, AbstractValueFactory factory, DexType type) {
- return join(other, factory, type, false);
+ return join(other, factory, type.isReferenceType(), false);
}
+ // TODO(b/196321452): Clean this up, in particular, replace the "allow" parameters by a
+ // configuration object.
public AbstractValue join(
AbstractValue other,
AbstractValueFactory factory,
- DexType type,
+ boolean allowNullOrAbstractValue,
boolean allowNonConstantNumbers) {
if (isBottom() || other.isUnknown()) {
return other;
@@ -159,7 +169,7 @@
if (equals(other)) {
return this;
}
- if (type.isReferenceType()) {
+ if (allowNullOrAbstractValue) {
if (isNull()) {
return NullOrAbstractValue.create(other);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index 0d9d21a..6c22403 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -15,8 +15,8 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.LookupResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
import com.android.tools.r8.ir.analysis.modeling.LibraryMethodReadSetModeling;
@@ -115,7 +115,8 @@
refinedReceiverLowerBound = null;
}
}
- ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method, getInterfaceBit());
+ MethodResolutionResult resolutionResult =
+ appView.appInfo().resolveMethod(method, getInterfaceBit());
LookupResult lookupResult;
if (refinedReceiverUpperBound != null) {
lookupResult =
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
index 6a83961..38e86e1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
@@ -10,8 +10,8 @@
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.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
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 9313b59..f1f86b3 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
@@ -13,8 +13,8 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
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 50899ca..05fba04 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
@@ -13,8 +13,8 @@
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.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
@@ -184,7 +184,7 @@
}
// Verify that the object does not have a finalizer.
- ResolutionResult finalizeResolutionResult =
+ MethodResolutionResult finalizeResolutionResult =
appViewWithLiveness
.appInfo()
.resolveMethodOnClass(dexItemFactory.objectMembers.finalize, clazz);
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index d582fdb..ce8efd1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
@@ -1083,6 +1084,10 @@
return type;
}
+ public DynamicType getDynamicType(AppView<AppInfoWithLiveness> appView) {
+ return DynamicType.create(this, appView);
+ }
+
public TypeElement getDynamicUpperBoundType(
AppView<? extends AppInfoWithClassHierarchy> appView) {
Value root = getAliasedValue();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
index 48656ec..1e5a344 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
@@ -19,9 +19,9 @@
import com.android.tools.r8.graph.FieldAccessInfoCollection;
import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
import com.android.tools.r8.graph.LookupResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.conversion.CallGraph.Node;
@@ -173,7 +173,7 @@
Invoke.Type type = result.getType();
if (type == Invoke.Type.INTERFACE || type == Invoke.Type.VIRTUAL) {
// For virtual and interface calls add all potential targets that could be called.
- ResolutionResult resolutionResult =
+ MethodResolutionResult resolutionResult =
appView.appInfo().resolveMethod(method, type == Invoke.Type.INTERFACE);
DexEncodedMethod target = resolutionResult.getSingleTarget();
if (target != null) {
@@ -215,7 +215,8 @@
possibleProgramTargetsCache.computeIfAbsent(
target,
method -> {
- ResolutionResult resolution = appView.appInfo().resolveMethod(method, isInterface);
+ MethodResolutionResult resolution =
+ appView.appInfo().resolveMethod(method, isInterface);
if (resolution.isVirtualTarget()) {
LookupResult lookupResult =
resolution.lookupVirtualDispatchTargets(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 5a492f7..0085b70 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -199,6 +199,7 @@
Instruction throwing = instructions.removeLast();
assert throwing.isThrow();
UninitializedThisLocalRead read = new UninitializedThisLocalRead(code.getThis());
+ read.setPosition(throwing.getPosition());
uninitializedThisLocalReads.add(read);
read.setBlock(exitBlock);
instructions.addLast(read);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
index 136d452..3129b82 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
@@ -76,6 +76,11 @@
D8CfInstructionDesugaringEventConsumer instructionDesugaringEventConsumer =
CfInstructionDesugaringEventConsumer.createForD8(methodProcessor);
+ // TODO(b/191656218): Move upfront the loop and use maybe the class event consumer.
+ if (appView.options().isDesugaredLibraryCompilation()) {
+ converter.ensureWrappersForL8(instructionDesugaringEventConsumer);
+ }
+
// Process the wave and wait for all IR processing to complete.
methodProcessor.newWave();
ThreadUtils.processItems(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 2563fa8..3911614 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -117,6 +117,7 @@
import com.android.tools.r8.ir.code.Xor;
import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
+import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorIROptimizer;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.Pair;
@@ -734,9 +735,23 @@
// will be passed during (double) inlining. Instead of adding assumptions and removing invalid
// ones, it's better not to insert assumptions for inlinee in the beginning.
CallSiteOptimizationInfo callSiteOptimizationInfo = getMethod().getCallSiteOptimizationInfo();
- if (method == context && appView.callSiteOptimizationInfoPropagator() != null) {
- appView.callSiteOptimizationInfoPropagator()
- .applyCallSiteOptimizationInfo(ir, callSiteOptimizationInfo);
+ if (callSiteOptimizationInfo.isConcreteCallSiteOptimizationInfo() && method == context) {
+ // TODO(b/190154391): Consider pruning all argument information from the optimization info
+ // after the second optimization pass. That way we save memory and can assert here that
+ // !appView.hasLiveness() (which currently may happen due to the reflective behavior
+ // handling in the final round of tree shaking).
+ if (appView.hasLiveness()) {
+ if (appView
+ .options()
+ .callSiteOptimizationOptions()
+ .isExperimentalArgumentPropagationEnabled()
+ || appView.callSiteOptimizationInfoPropagator().getMode().isRevisit()) {
+ ArgumentPropagatorIROptimizer.optimize(
+ appView.withLiveness(),
+ ir,
+ callSiteOptimizationInfo.asConcreteCallSiteOptimizationInfo());
+ }
+ }
}
if (appView.options().isStringSwitchConversionEnabled()) {
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 3144113..73ef0f1 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
@@ -55,7 +55,6 @@
import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformer;
import com.android.tools.r8.ir.desugar.ProgramAdditions;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter.Mode;
import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceApplicationRewriter;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
@@ -96,9 +95,9 @@
import com.android.tools.r8.ir.optimize.string.StringOptimizer;
import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
-import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.IdentifierNameStringMarker;
+import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.LibraryMethodOverrideAnalysis;
@@ -156,7 +155,6 @@
private final CovariantReturnTypeAnnotationTransformer covariantReturnTypeAnnotationTransformer;
private final StringSwitchRemover stringSwitchRemover;
private final TypeChecker typeChecker;
- private final DesugaredLibraryAPIConverter desugaredLibraryAPIConverter;
private final ServiceLoaderRewriter serviceLoaderRewriter;
private final EnumValueOptimizer enumValueOptimizer;
private final EnumUnboxer enumUnboxer;
@@ -226,10 +224,8 @@
// - invoke-special desugaring.
assert options.desugarState.isOn();
this.instructionDesugaring = CfInstructionDesugaringCollection.create(appView);
- this.classDesugaring = instructionDesugaring.createClassDesugaringCollection();
+ this.classDesugaring = CfClassDesugaringCollection.create(appView);
this.interfaceMethodRewriter = null;
- this.desugaredLibraryAPIConverter =
- new DesugaredLibraryAPIConverter(appView, Mode.GENERATE_CALLBACKS_AND_WRAPPERS);
this.covariantReturnTypeAnnotationTransformer = null;
this.dynamicTypeOptimization = null;
this.classInliner = null;
@@ -255,7 +251,10 @@
appView.enableWholeProgramOptimizations()
? CfInstructionDesugaringCollection.empty()
: CfInstructionDesugaringCollection.create(appView);
- this.classDesugaring = instructionDesugaring.createClassDesugaringCollection();
+ this.classDesugaring =
+ appView.enableWholeProgramOptimizations()
+ ? CfClassDesugaringCollection.empty()
+ : CfClassDesugaringCollection.create(appView);
this.interfaceMethodRewriter =
options.isInterfaceMethodDesugaringEnabled() && appView.enableWholeProgramOptimizations()
? new InterfaceMethodRewriter(appView, this)
@@ -300,11 +299,6 @@
options.enableServiceLoaderRewriting
? new ServiceLoaderRewriter(appViewWithLiveness)
: null;
- this.desugaredLibraryAPIConverter =
- appView.rewritePrefix.isRewriting()
- ? new DesugaredLibraryAPIConverter(
- appView, Mode.ASSERT_CALLBACKS_AND_WRAPPERS_GENERATED)
- : null;
this.enumValueOptimizer =
options.enableEnumValueOptimization ? new EnumValueOptimizer(appViewWithLiveness) : null;
} else {
@@ -321,10 +315,6 @@
this.identifierNameStringMarker = null;
this.devirtualizer = null;
this.typeChecker = null;
- this.desugaredLibraryAPIConverter =
- appView.rewritePrefix.isRewriting()
- ? new DesugaredLibraryAPIConverter(appView, Mode.GENERATE_CALLBACKS_AND_WRAPPERS)
- : null;
this.serviceLoaderRewriter = null;
this.methodOptimizationInfoCollector = null;
this.enumValueOptimizer = null;
@@ -365,6 +355,13 @@
D8NestBasedAccessDesugaring::clearNestAttributes);
}
+ public void ensureWrappersForL8(
+ D8CfInstructionDesugaringEventConsumer instructionDesugaringEventConsumer) {
+ assert appView.options().isDesugaredLibraryCompilation();
+ instructionDesugaring.withDesugaredLibraryAPIConverter(
+ converter -> converter.ensureWrappersForL8(instructionDesugaringEventConsumer));
+ }
+
private void staticizeClasses(
OptimizationFeedback feedback, ExecutorService executorService, GraphLens applied)
throws ExecutionException {
@@ -430,7 +427,6 @@
new EmulatedInterfaceApplicationRewriter(appView).rewriteApplication(builder);
}
processCovariantReturnTypeAnnotations(builder);
- generateDesugaredLibraryAPIWrappers(builder, executor);
timing.end();
@@ -457,13 +453,14 @@
D8MethodProcessor methodProcessor, ExecutorService executorService)
throws ExecutionException {
D8CfPostProcessingDesugaringEventConsumer eventConsumer =
- CfPostProcessingDesugaringEventConsumer.createForD8(methodProcessor, appView);
+ CfPostProcessingDesugaringEventConsumer.createForD8(methodProcessor);
methodProcessor.newWave();
InterfaceMethodProcessorFacade interfaceDesugaring =
instructionDesugaring.getInterfaceMethodPostProcessingDesugaring(ExcludeDexResources);
CfPostProcessingDesugaringCollection.create(
appView, interfaceDesugaring, instructionDesugaring.getRetargetingInfo())
- .postProcessingDesugaring(eventConsumer, executorService);
+ .postProcessingDesugaring(appView.appInfo().classes(), eventConsumer, executorService);
+ methodProcessor.awaitMethodProcessing();
eventConsumer.finalizeDesugaring();
}
@@ -481,6 +478,9 @@
rewriteEnclosingLambdaMethodAttributes(
appView, classConverterResult.getForcefullyMovedLambdaMethods());
+
+ instructionDesugaring.withDesugaredLibraryAPIConverter(
+ DesugaredLibraryAPIConverter::generateTrackingWarnings);
}
public void desugarClassesForD8(
@@ -589,32 +589,14 @@
}
}
- private boolean needsIRConversion(ProgramMethod method) {
+ private boolean needsIRConversion() {
if (appView.enableWholeProgramOptimizations()) {
return true;
}
if (options.testing.forceIRForCfToCfDesugar) {
return true;
}
- if (options.isDesugaredLibraryCompilation()) {
- return true;
- }
- if (!options.cfToCfDesugar) {
- return true;
- }
- if (desugaredLibraryAPIConverter != null
- && desugaredLibraryAPIConverter.shouldRegisterCallback(method)) {
- return true;
- }
- if (method.getDefinition().getCode() instanceof SynthesizedCode) {
- // SynthesizedCode needs IR to generate the code.
- return true;
- } else {
- NeedsIRDesugarUseRegistry useRegistry =
- new NeedsIRDesugarUseRegistry(method, appView, desugaredLibraryAPIConverter);
- method.registerCodeReferences(useRegistry);
- return useRegistry.needsDesugaring();
- }
+ return !options.cfToCfDesugar;
}
private void checkPrefixMerging(ProgramMethod method) {
@@ -691,6 +673,8 @@
printPhase("Primary optimization pass");
+ // Setup the argument propagator for the primary optimization pass.
+ appView.withArgumentPropagator(ArgumentPropagator::initializeCodeScanner);
appView.withCallSiteOptimizationInfoPropagator(
optimization -> {
optimization.abandonCallSitePropagationForLambdaImplementationMethods(
@@ -736,11 +720,21 @@
// Assure that no more optimization feedback left after primary processing.
assert feedback.noUpdatesLeft();
appView.setAllCodeProcessed();
+
// All the code has been processed so the rewriting required by the lenses is done everywhere,
// we clear lens code rewriting so that the lens rewriter can be re-executed in phase 2 if new
// lenses with code rewriting are added.
appView.clearCodeRewritings();
+ // Analyze the data collected by the argument propagator, use the analysis result to update
+ // the parameter optimization infos, and rewrite the application.
+ appView.withArgumentPropagator(
+ argumentPropagator -> {
+ argumentPropagator.populateParameterOptimizationInfo(executorService);
+ argumentPropagator.optimizeMethodParameters();
+ argumentPropagator.enqueueMethodsForProcessing(postMethodProcessorBuilder);
+ });
+
if (libraryMethodOverrideAnalysis != null) {
libraryMethodOverrideAnalysis.finish();
}
@@ -818,9 +812,6 @@
runInterfaceDesugaringProcessorsForR8(IncludeAllResources, executorService);
feedback.updateVisibleOptimizationInfo();
- printPhase("Desugared library API Conversion finalization");
- generateDesugaredLibraryAPIWrappers(builder, executorService);
-
if (serviceLoaderRewriter != null) {
processSynthesizedServiceLoaderMethods(
serviceLoaderRewriter.getServiceLoadMethods(), executorService);
@@ -984,14 +975,6 @@
removeDeadCodeAndFinalizeIR(code, OptimizationFeedbackIgnore.getInstance(), Timing.empty());
}
- private void generateDesugaredLibraryAPIWrappers(
- DexApplication.Builder<?> builder, ExecutorService executorService)
- throws ExecutionException {
- if (desugaredLibraryAPIConverter != null) {
- desugaredLibraryAPIConverter.finalizeWrappers(builder, this, executorService);
- }
- }
-
private void clearDexMethodCompilationState() {
appView.appInfo().classes().forEach(this::clearDexMethodCompilationState);
}
@@ -1159,7 +1142,7 @@
options.testing.hookInIrConversion.run();
}
- if (!needsIRConversion(method) || options.skipIR) {
+ if (!needsIRConversion() || options.skipIR) {
feedback.markProcessed(method.getDefinition(), ConstraintWithTarget.NEVER);
return Timing.empty();
}
@@ -1509,23 +1492,9 @@
timing.end();
}
- previous = printMethod(code, "IR after interface method rewriting (SSA)", previous);
-
- // This pass has to be after interfaceMethodRewriter and BackportedMethodRewriter.
- if (desugaredLibraryAPIConverter != null
- && (!appView.enableWholeProgramOptimizations()
- || methodProcessor.isPrimaryMethodProcessor())) {
- timing.begin("Desugar library API");
- desugaredLibraryAPIConverter.desugar(code);
- timing.end();
- assert code.isConsistentSSA();
- }
-
- previous = printMethod(code, "IR after desugared library API Conversion (SSA)", previous);
-
assert code.verifyTypes(appView);
- previous = printMethod(code, "IR after twr close resource rewriter (SSA)", previous);
+ previous = printMethod(code, "IR after interface method rewriting (SSA)", previous);
// TODO(b/140766440): an ideal solution would be puttting CodeOptimization for this into
// the list for primary processing only.
@@ -1637,6 +1606,8 @@
MethodProcessor methodProcessor,
MutableMethodConversionOptions conversionOptions,
Timing timing) {
+ appView.withArgumentPropagator(
+ argumentPropagator -> argumentPropagator.scan(method, code, methodProcessor));
if (enumUnboxer != null && methodProcessor.isPrimaryMethodProcessor()) {
enumUnboxer.analyzeEnums(code, conversionOptions);
@@ -1666,8 +1637,7 @@
timing.end();
}
- if (appView.appInfo().withLiveness().isPinned(code.context().getReference())
- || !appView.options().isOptimizing()) {
+ if (appView.getKeepInfo().getMethodInfo(code.context()).isPinned(options)) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java b/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
deleted file mode 100644
index 0d1c587..0000000
--- a/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
+++ /dev/null
@@ -1,108 +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.ir.conversion;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexCallSite;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.UseRegistry;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
-
-class NeedsIRDesugarUseRegistry extends UseRegistry {
-
- private boolean needsDesugaring = false;
- private final ProgramMethod context;
- private final DesugaredLibraryAPIConverter desugaredLibraryAPIConverter;
-
- public NeedsIRDesugarUseRegistry(
- ProgramMethod method,
- AppView<?> appView,
- DesugaredLibraryAPIConverter desugaredLibraryAPIConverter) {
- super(appView.dexItemFactory());
- this.context = method;
- this.desugaredLibraryAPIConverter = desugaredLibraryAPIConverter;
- }
-
- public boolean needsDesugaring() {
- return needsDesugaring;
- }
-
- @Override
- public void registerInitClass(DexType type) {
- if (!needsDesugaring
- && desugaredLibraryAPIConverter != null
- && desugaredLibraryAPIConverter.canConvert(type)) {
- needsDesugaring = true;
- }
- }
-
- @Override
- public void registerInvokeVirtual(DexMethod method) {
- registerDesugaredLibraryAPIConverter(method);
- }
-
- @Override
- public void registerInvokeDirect(DexMethod method) {
- registerDesugaredLibraryAPIConverter(method);
- }
-
- private void registerDesugaredLibraryAPIConverter(DexMethod method) {
- if (!needsDesugaring) {
- needsDesugaring =
- desugaredLibraryAPIConverter != null
- && desugaredLibraryAPIConverter.shouldRewriteInvoke(method);
- }
- }
-
- @Override
- public void registerInvokeStatic(DexMethod method) {
- registerDesugaredLibraryAPIConverter(method);
- }
-
- @Override
- public void registerInvokeInterface(DexMethod method) {
- registerDesugaredLibraryAPIConverter(method);
- }
-
- @Override
- public void registerInvokeStatic(DexMethod method, boolean itf) {
- registerInvokeStatic(method);
- }
-
- @Override
- public void registerCallSite(DexCallSite callSite) {
- super.registerCallSite(callSite);
- needsDesugaring = true;
- }
-
- @Override
- public void registerInvokeSuper(DexMethod method) {
- registerDesugaredLibraryAPIConverter(method);
- }
-
- @Override
- public void registerInstanceFieldRead(DexField field) {}
-
- @Override
- public void registerInstanceFieldWrite(DexField field) {}
-
- @Override
- public void registerNewInstance(DexType type) {}
-
- @Override
- public void registerStaticFieldRead(DexField field) {}
-
- @Override
- public void registerStaticFieldWrite(DexField field) {}
-
- @Override
- public void registerTypeReference(DexType type) {}
-
- @Override
- public void registerInstanceOf(DexType type) {}
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfClassDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfClassDesugaringCollection.java
index 70d5123..1ddeb09 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfClassDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfClassDesugaringCollection.java
@@ -4,8 +4,12 @@
package com.android.tools.r8.ir.desugar;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.ir.desugar.records.RecordRewriter;
+import com.google.common.collect.Iterables;
+import java.util.ArrayList;
+import java.util.List;
/** Interface for desugaring a class. */
public abstract class CfClassDesugaringCollection {
@@ -17,21 +21,39 @@
public abstract boolean isEmpty();
- public static class NonEmptyCfClassDesugaringCollection extends CfClassDesugaringCollection {
- private final RecordRewriter recordRewriter;
+ public static CfClassDesugaringCollection empty() {
+ return EmptyCfClassDesugaringCollection.getInstance();
+ }
- NonEmptyCfClassDesugaringCollection(RecordRewriter recordRewriter) {
- this.recordRewriter = recordRewriter;
+ public static CfClassDesugaringCollection create(AppView<?> appView) {
+ List<CfClassDesugaring> desugarings = new ArrayList<>();
+ RecordRewriter recordRewriter = RecordRewriter.create(appView);
+ if (recordRewriter != null) {
+ desugarings.add(recordRewriter);
+ }
+ if (desugarings.isEmpty()) {
+ return empty();
+ }
+ return new NonEmptyCfClassDesugaringCollection(desugarings);
+ }
+
+ public static class NonEmptyCfClassDesugaringCollection extends CfClassDesugaringCollection {
+ private final List<CfClassDesugaring> desugarings;
+
+ NonEmptyCfClassDesugaringCollection(List<CfClassDesugaring> desugarings) {
+ this.desugarings = desugarings;
}
@Override
public void desugar(DexProgramClass clazz, CfClassDesugaringEventConsumer eventConsumer) {
- recordRewriter.desugar(clazz, eventConsumer);
+ for (CfClassDesugaring desugaring : desugarings) {
+ desugaring.desugar(clazz, eventConsumer);
+ }
}
@Override
public boolean needsDesugaring(DexProgramClass clazz) {
- return recordRewriter.needsDesugaring(clazz);
+ return Iterables.any(desugarings, desugaring -> desugaring.needsDesugaring(clazz));
}
@Override
@@ -41,6 +63,14 @@
}
public static class EmptyCfClassDesugaringCollection extends CfClassDesugaringCollection {
+
+ private static final EmptyCfClassDesugaringCollection INSTANCE =
+ new EmptyCfClassDesugaringCollection();
+
+ public static EmptyCfClassDesugaringCollection getInstance() {
+ return INSTANCE;
+ }
+
@Override
public void desugar(DexProgramClass clazz, CfClassDesugaringEventConsumer eventConsumer) {
// Intentionally empty.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
index 8512734..10032bc 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
@@ -7,11 +7,13 @@
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor;
import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
import com.android.tools.r8.utils.ThrowingConsumer;
+import java.util.function.Consumer;
/**
* Abstracts a collection of low-level desugarings (i.e., mappings from class-file instructions to
@@ -52,8 +54,6 @@
return false;
}
- public abstract CfClassDesugaringCollection createClassDesugaringCollection();
-
/** Returns true if the given method needs desugaring. */
public abstract boolean needsDesugaring(ProgramMethod method);
@@ -64,4 +64,7 @@
Flavor flavor);
public abstract RetargetingInfo getRetargetingInfo();
+
+ public abstract void withDesugaredLibraryAPIConverter(
+ Consumer<DesugaredLibraryAPIConverter> consumer);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index d497332..0b29d90 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.ir.conversion.ClassConverterResult;
import com.android.tools.r8.ir.conversion.D8MethodProcessor;
import com.android.tools.r8.ir.desugar.backports.BackportedMethodDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverterEventConsumer;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterInstructionEventConsumer;
import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialBridgeInfo;
import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialToSelfDesugaringEventConsumer;
@@ -48,7 +49,8 @@
RecordDesugaringEventConsumer,
TwrCloseResourceDesugaringEventConsumer,
InterfaceMethodDesugaringEventConsumer,
- DesugaredLibraryRetargeterInstructionEventConsumer {
+ DesugaredLibraryRetargeterInstructionEventConsumer,
+ DesugaredLibraryAPIConverterEventConsumer {
public static D8CfInstructionDesugaringEventConsumer createForD8(
D8MethodProcessor methodProcessor) {
@@ -68,6 +70,21 @@
return new CfInstructionDesugaringEventConsumer() {
@Override
+ public void acceptWrapperProgramClass(DexProgramClass clazz) {
+ assert false;
+ }
+
+ @Override
+ public void acceptWrapperClasspathClass(DexClasspathClass clazz) {
+ assert false;
+ }
+
+ @Override
+ public void acceptAPIConversion(ProgramMethod method) {
+ assert false;
+ }
+
+ @Override
public void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz) {
assert false;
}
@@ -229,6 +246,21 @@
methodProcessor.scheduleDesugaredMethodForProcessing(method);
}
+ @Override
+ public void acceptWrapperProgramClass(DexProgramClass clazz) {
+ methodProcessor.scheduleDesugaredMethodsForProcessing(clazz.programMethods());
+ }
+
+ @Override
+ public void acceptWrapperClasspathClass(DexClasspathClass clazz) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void acceptAPIConversion(ProgramMethod method) {
+ methodProcessor.scheduleDesugaredMethodForProcessing(method);
+ }
+
public List<ProgramMethod> finalizeDesugaring(
AppView<?> appView, ClassConverterResult.Builder classConverterResultBuilder) {
List<ProgramMethod> needsProcessing = new ArrayList<>();
@@ -348,6 +380,23 @@
}
@Override
+ public void acceptWrapperProgramClass(DexProgramClass clazz) {
+ // Called only in Desugared library compilation which is D8.
+ assert false;
+ }
+
+ @Override
+ public void acceptWrapperClasspathClass(DexClasspathClass clazz) {
+ additions.addLiveClasspathClass(clazz);
+ }
+
+ @Override
+ public void acceptAPIConversion(ProgramMethod method) {
+ // Intentionally empty. The method will be hit by the tracing in R8 as if it was
+ // present in the input code, and thus nothing needs to be done.
+ }
+
+ @Override
public void acceptBackportedMethod(ProgramMethod backportedMethod, ProgramMethod context) {
// Intentionally empty. The backported method will be hit by the tracing in R8 as if it was
// present in the input code, and thus nothing needs to be done.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaring.java
index 38a0413..f3aaaad 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaring.java
@@ -3,12 +3,16 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.desugar;
+import com.android.tools.r8.graph.DexProgramClass;
+import java.util.Collection;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
public interface CfPostProcessingDesugaring {
void postProcessingDesugaring(
- CfPostProcessingDesugaringEventConsumer eventConsumer, ExecutorService executorService)
+ Collection<DexProgramClass> programClasses,
+ CfPostProcessingDesugaringEventConsumer eventConsumer,
+ ExecutorService executorService)
throws ExecutionException;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
index 986c6ea..e539861 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
@@ -4,10 +4,13 @@
package com.android.tools.r8.ir.desugar;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPICallbackSynthesizor;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterPostProcessor;
import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -30,7 +33,9 @@
}
public abstract void postProcessingDesugaring(
- CfPostProcessingDesugaringEventConsumer eventConsumer, ExecutorService executorService)
+ Collection<DexProgramClass> programClasses,
+ CfPostProcessingDesugaringEventConsumer eventConsumer,
+ ExecutorService executorService)
throws ExecutionException;
public static class NonEmptyCfPostProcessingDesugaringCollection
@@ -47,10 +52,6 @@
AppView<?> appView,
InterfaceMethodProcessorFacade interfaceMethodProcessorFacade,
RetargetingInfo retargetingInfo) {
- if (appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()
- && interfaceMethodProcessorFacade == null) {
- return empty();
- }
ArrayList<CfPostProcessingDesugaring> desugarings = new ArrayList<>();
if (!appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) {
desugarings.add(new DesugaredLibraryRetargeterPostProcessor(appView, retargetingInfo));
@@ -58,15 +59,29 @@
if (interfaceMethodProcessorFacade != null) {
desugarings.add(interfaceMethodProcessorFacade);
}
+ DesugaredLibraryAPICallbackSynthesizor apiCallbackSynthesizor =
+ appView.rewritePrefix.isRewriting()
+ ? new DesugaredLibraryAPICallbackSynthesizor(appView)
+ : null;
+ // At this point the desugaredLibraryAPIConverter is required to be last to generate
+ // call-backs on the forwarding methods.
+ if (apiCallbackSynthesizor != null) {
+ desugarings.add(apiCallbackSynthesizor);
+ }
+ if (desugarings.isEmpty()) {
+ return empty();
+ }
return new NonEmptyCfPostProcessingDesugaringCollection(desugarings);
}
@Override
public void postProcessingDesugaring(
- CfPostProcessingDesugaringEventConsumer eventConsumer, ExecutorService executorService)
+ Collection<DexProgramClass> programClasses,
+ CfPostProcessingDesugaringEventConsumer eventConsumer,
+ ExecutorService executorService)
throws ExecutionException {
for (CfPostProcessingDesugaring desugaring : desugarings) {
- desugaring.postProcessingDesugaring(eventConsumer, executorService);
+ desugaring.postProcessingDesugaring(programClasses, eventConsumer, executorService);
}
}
}
@@ -85,7 +100,9 @@
@Override
public void postProcessingDesugaring(
- CfPostProcessingDesugaringEventConsumer eventConsumer, ExecutorService executorService)
+ Collection<DexProgramClass> programClasses,
+ CfPostProcessingDesugaringEventConsumer eventConsumer,
+ ExecutorService executorService)
throws ExecutionException {
// Intentionally empty.
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
index 5734cab..d0b798b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
@@ -3,13 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.desugar;
-import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.D8MethodProcessor;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverterEventConsumer.DesugaredLibraryAPIConverterPostProcessingEventConsumer;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterInstructionEventConsumer.DesugaredLibraryRetargeterPostProcessingEventConsumer;
import com.android.tools.r8.ir.desugar.itf.InterfaceProcessingDesugaringEventConsumer;
import com.android.tools.r8.shaking.Enqueuer.SyntheticAdditions;
@@ -23,21 +22,16 @@
*/
public abstract class CfPostProcessingDesugaringEventConsumer
implements DesugaredLibraryRetargeterPostProcessingEventConsumer,
- InterfaceProcessingDesugaringEventConsumer {
- protected DesugaredLibraryAPIConverter desugaredLibraryAPIConverter;
-
- protected CfPostProcessingDesugaringEventConsumer(AppView<?> appView) {
- this.desugaredLibraryAPIConverter = new DesugaredLibraryAPIConverter(appView, null);
- }
+ InterfaceProcessingDesugaringEventConsumer,
+ DesugaredLibraryAPIConverterPostProcessingEventConsumer {
public static D8CfPostProcessingDesugaringEventConsumer createForD8(
- D8MethodProcessor methodProcessor, AppView<?> appView) {
- return new D8CfPostProcessingDesugaringEventConsumer(methodProcessor, appView);
+ D8MethodProcessor methodProcessor) {
+ return new D8CfPostProcessingDesugaringEventConsumer(methodProcessor);
}
- public static R8PostProcessingDesugaringEventConsumer createForR8(
- AppView<?> appView, SyntheticAdditions additions) {
- return new R8PostProcessingDesugaringEventConsumer(appView, additions);
+ public static R8PostProcessingDesugaringEventConsumer createForR8(SyntheticAdditions additions) {
+ return new R8PostProcessingDesugaringEventConsumer(additions);
}
public abstract void finalizeDesugaring() throws ExecutionException;
@@ -49,9 +43,7 @@
// concurrently processing other methods.
private final ProgramMethodSet methodsToReprocess = ProgramMethodSet.createConcurrent();
- private D8CfPostProcessingDesugaringEventConsumer(
- D8MethodProcessor methodProcessor, AppView<?> appView) {
- super(appView);
+ private D8CfPostProcessingDesugaringEventConsumer(D8MethodProcessor methodProcessor) {
this.methodProcessor = methodProcessor;
}
@@ -92,21 +84,24 @@
methodProcessor.scheduleDesugaredMethodsForProcessing(methodsToReprocess);
methodProcessor.awaitMethodProcessing();
}
+
+ @Override
+ public void acceptAPIConversionCallback(ProgramMethod method) {
+ methodsToReprocess.add(method);
+ }
}
public static class R8PostProcessingDesugaringEventConsumer
extends CfPostProcessingDesugaringEventConsumer {
private final SyntheticAdditions additions;
- protected R8PostProcessingDesugaringEventConsumer(
- AppView<?> appView, SyntheticAdditions additions) {
- super(appView);
+ R8PostProcessingDesugaringEventConsumer(SyntheticAdditions additions) {
this.additions = additions;
}
@Override
public void finalizeDesugaring() throws ExecutionException {
- desugaredLibraryAPIConverter.generateTrackingWarnings();
+ // Intentionally empty.
}
@Override
@@ -127,10 +122,6 @@
@Override
public void acceptForwardingMethod(ProgramMethod method) {
additions.addLiveMethod(method);
- ProgramMethod callback = desugaredLibraryAPIConverter.generateCallbackIfRequired(method);
- if (callback != null) {
- additions.addLiveMethod(callback);
- }
}
@Override
@@ -142,5 +133,10 @@
public void acceptEmulatedInterfaceMethod(ProgramMethod method) {
assert false : "TODO(b/183998768): Support Interface processing in R8";
}
+
+ @Override
+ public void acceptAPIConversionCallback(ProgramMethod method) {
+ additions.addLiveMethod(method);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
index 7991ac3..b23f31e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
@@ -6,12 +6,13 @@
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.desugar.CfClassDesugaringCollection.EmptyCfClassDesugaringCollection;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor;
import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
import com.android.tools.r8.utils.ThrowingConsumer;
+import java.util.function.Consumer;
public class EmptyCfInstructionDesugaringCollection extends CfInstructionDesugaringCollection {
@@ -49,11 +50,6 @@
}
@Override
- public CfClassDesugaringCollection createClassDesugaringCollection() {
- return new EmptyCfClassDesugaringCollection();
- }
-
- @Override
public boolean needsDesugaring(ProgramMethod method) {
return false;
}
@@ -73,4 +69,9 @@
public RetargetingInfo getRetargetingInfo() {
return null;
}
+
+ @Override
+ public void withDesugaredLibraryAPIConverter(Consumer<DesugaredLibraryAPIConverter> consumer) {
+ // Intentionally empty.
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 2d53448..88f451b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -29,10 +29,10 @@
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.MethodResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.desugar.lambda.ForcefullyMovedLambdaMethodConsumer;
@@ -321,7 +321,7 @@
assert implMethod.holder == accessedFrom.getHolderType();
assert descriptor.verifyTargetFoundInClass(accessedFrom.getHolderType());
if (implHandle.type.isInvokeStatic()) {
- ResolutionResult resolution =
+ MethodResolutionResult resolution =
appView.appInfoForDesugaring().resolveMethod(implMethod, implHandle.isInterface);
if (resolution.isFailedResolution()) {
return new InvalidLambdaImplTarget(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
index 6975252..e0a9c07 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
@@ -407,7 +407,7 @@
}
DexType fromTypeAsPrimitive = factory.getPrimitiveFromBoxed(boxedType);
if (fromTypeAsPrimitive != null) {
- addPrimitiveUnboxing(fromTypeAsPrimitive, boxedType, instructions, factory);
+ addPrimitiveUnboxing(boxedType, instructions, factory);
addPrimitiveWideningConversion(fromTypeAsPrimitive, toType, instructions);
return;
}
@@ -424,7 +424,7 @@
|| (boxedFromType != factory.booleanType
&& boxedFromType != factory.charType
&& toType == factory.boxedNumberType)) {
- addPrimitiveBoxing(fromType, boxedFromType, instructions, factory);
+ addPrimitiveBoxing(boxedFromType, instructions, factory);
return;
}
}
@@ -513,55 +513,19 @@
"converted to " + toType.toSourceString() + " via primitive widening conversion.");
}
- private static DexMethod getUnboxMethod(byte primitive, DexType boxType, DexItemFactory factory) {
- DexProto proto;
- switch (primitive) {
- case 'Z': // byte
- proto = factory.createProto(factory.booleanType);
- return factory.createMethod(boxType, proto, factory.unboxBooleanMethodName);
- case 'B': // byte
- proto = factory.createProto(factory.byteType);
- return factory.createMethod(boxType, proto, factory.unboxByteMethodName);
- case 'S': // short
- proto = factory.createProto(factory.shortType);
- return factory.createMethod(boxType, proto, factory.unboxShortMethodName);
- case 'C': // char
- proto = factory.createProto(factory.charType);
- return factory.createMethod(boxType, proto, factory.unboxCharMethodName);
- case 'I': // int
- proto = factory.createProto(factory.intType);
- return factory.createMethod(boxType, proto, factory.unboxIntMethodName);
- case 'J': // long
- proto = factory.createProto(factory.longType);
- return factory.createMethod(boxType, proto, factory.unboxLongMethodName);
- case 'F': // float
- proto = factory.createProto(factory.floatType);
- return factory.createMethod(boxType, proto, factory.unboxFloatMethodName);
- case 'D': // double
- proto = factory.createProto(factory.doubleType);
- return factory.createMethod(boxType, proto, factory.unboxDoubleMethodName);
- default:
- throw new Unreachable("Invalid primitive type descriptor: " + primitive);
- }
- }
-
private static void addPrimitiveUnboxing(
- DexType primitiveType,
DexType boxType,
Builder<CfInstruction> instructions,
DexItemFactory factory) {
- DexMethod method = getUnboxMethod(primitiveType.descriptor.content[0], boxType, factory);
+ DexMethod method = factory.getUnboxPrimitiveMethod(boxType);
instructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, method, false));
}
private static void addPrimitiveBoxing(
- DexType primitiveType,
DexType boxType,
Builder<CfInstruction> instructions,
DexItemFactory factory) {
- // Generate factory method fo boxing.
- DexProto proto = factory.createProto(boxType, primitiveType);
- DexMethod method = factory.createMethod(boxType, proto, factory.valueOfMethodName);
+ DexMethod method = factory.getBoxPrimitiveMethod(boxType);
instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, method, false));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index a7f1a33..c4ce73e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -12,8 +12,7 @@
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.desugar.CfClassDesugaringCollection.EmptyCfClassDesugaringCollection;
-import com.android.tools.r8.ir.desugar.CfClassDesugaringCollection.NonEmptyCfClassDesugaringCollection;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo;
import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialToSelfDesugaring;
@@ -35,6 +34,7 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
+import java.util.function.Consumer;
public class NonEmptyCfInstructionDesugaringCollection extends CfInstructionDesugaringCollection {
@@ -45,6 +45,7 @@
private final RecordRewriter recordRewriter;
private final DesugaredLibraryRetargeter desugaredLibraryRetargeter;
private final InterfaceMethodRewriter interfaceMethodRewriter;
+ private final DesugaredLibraryAPIConverter desugaredLibraryAPIConverter;
NonEmptyCfInstructionDesugaringCollection(AppView<?> appView) {
this.appView = appView;
@@ -53,6 +54,7 @@
this.recordRewriter = null;
this.desugaredLibraryRetargeter = null;
this.interfaceMethodRewriter = null;
+ this.desugaredLibraryAPIConverter = null;
return;
}
this.nestBasedAccessDesugaring = NestBasedAccessDesugaring.create(appView);
@@ -72,14 +74,32 @@
desugarings.add(new TwrInstructionDesugaring(appView));
}
// TODO(b/183998768): Enable interface method rewriter cf to cf also in R8.
- interfaceMethodRewriter =
- appView.options().isInterfaceMethodDesugaringEnabled()
- && !appView.enableWholeProgramOptimizations()
+ if (appView.options().isInterfaceMethodDesugaringEnabled()
+ && !appView.enableWholeProgramOptimizations()) {
+ interfaceMethodRewriter =
+ new InterfaceMethodRewriter(
+ appView, backportedMethodRewriter, desugaredLibraryRetargeter);
+ desugarings.add(interfaceMethodRewriter);
+ } else {
+ interfaceMethodRewriter = null;
+ }
+ // In R8 interface method rewriting is performed in IR, we still need to filter
+ // out from API conversion methods desugared by the interface method rewriter.
+ InterfaceMethodRewriter enforcedInterfaceMethodRewriter =
+ interfaceMethodRewriter == null && appView.options().isInterfaceMethodDesugaringEnabled()
? new InterfaceMethodRewriter(
appView, backportedMethodRewriter, desugaredLibraryRetargeter)
+ : interfaceMethodRewriter;
+ desugaredLibraryAPIConverter =
+ appView.rewritePrefix.isRewriting()
+ ? new DesugaredLibraryAPIConverter(
+ appView,
+ enforcedInterfaceMethodRewriter,
+ desugaredLibraryRetargeter,
+ backportedMethodRewriter)
: null;
- if (interfaceMethodRewriter != null) {
- desugarings.add(interfaceMethodRewriter);
+ if (desugaredLibraryAPIConverter != null) {
+ desugarings.add(desugaredLibraryAPIConverter);
}
desugarings.add(new LambdaInstructionDesugaring(appView));
desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
@@ -220,14 +240,6 @@
return true;
}
- @Override
- public CfClassDesugaringCollection createClassDesugaringCollection() {
- if (recordRewriter == null) {
- return new EmptyCfClassDesugaringCollection();
- }
- return new NonEmptyCfClassDesugaringCollection(recordRewriter);
- }
-
private Collection<CfInstruction> desugarInstruction(
CfInstruction instruction,
FreshLocalProvider freshLocalProvider,
@@ -332,4 +344,11 @@
}
return null;
}
+
+ @Override
+ public void withDesugaredLibraryAPIConverter(Consumer<DesugaredLibraryAPIConverter> consumer) {
+ if (desugaredLibraryAPIConverter != null) {
+ consumer.accept(desugaredLibraryAPIConverter);
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPICallbackSynthesizor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPICallbackSynthesizor.java
new file mode 100644
index 0000000..cfcb9b0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPICallbackSynthesizor.java
@@ -0,0 +1,223 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.desugaredlibrary;
+
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter.generateTrackDesugaredAPIWarnings;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter.isAPIConversionSyntheticType;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter.methodWithVivifiedTypeInSignature;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexClass;
+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.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaring;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverterEventConsumer.DesugaredLibraryAPIConverterPostProcessingEventConsumer;
+import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterWrapperCfCodeProvider;
+import com.android.tools.r8.utils.OptionalBool;
+import com.android.tools.r8.utils.WorkList;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+
+public class DesugaredLibraryAPICallbackSynthesizor implements CfPostProcessingDesugaring {
+
+ private final AppView<?> appView;
+ private final DexItemFactory factory;
+
+ private final DesugaredLibraryWrapperSynthesizer wrapperSynthesizor;
+ private final Set<DexMethod> trackedCallBackAPIs;
+
+ public DesugaredLibraryAPICallbackSynthesizor(AppView<?> appView) {
+ this.appView = appView;
+ this.factory = appView.dexItemFactory();
+ this.wrapperSynthesizor = new DesugaredLibraryWrapperSynthesizer(appView);
+ if (appView.options().testing.trackDesugaredAPIConversions) {
+ trackedCallBackAPIs = Sets.newConcurrentHashSet();
+ } else {
+ trackedCallBackAPIs = null;
+ }
+ }
+
+ // TODO(b/191656218): Consider parallelizing post processing.
+ @Override
+ public void postProcessingDesugaring(
+ Collection<DexProgramClass> programClasses,
+ CfPostProcessingDesugaringEventConsumer eventConsumer,
+ ExecutorService executorService) {
+ assert noPendingWrappersOrConversions();
+ for (DexProgramClass clazz : programClasses) {
+ if (!appView.isAlreadyLibraryDesugared(clazz)) {
+ ArrayList<DexEncodedMethod> callbacks = new ArrayList<>();
+ // We note that methods requiring callbacks are library overrides, therefore, they should
+ // always be live in R8.
+ for (ProgramMethod virtualProgramMethod : clazz.virtualProgramMethods()) {
+ if (shouldRegisterCallback(virtualProgramMethod)) {
+ if (trackedCallBackAPIs != null) {
+ trackedCallBackAPIs.add(virtualProgramMethod.getReference());
+ }
+ ProgramMethod callback =
+ generateCallbackMethod(
+ virtualProgramMethod.getDefinition(),
+ virtualProgramMethod.getHolder(),
+ eventConsumer);
+ callbacks.add(callback.getDefinition());
+ }
+ }
+ if (!callbacks.isEmpty()) {
+ clazz.addVirtualMethods(callbacks);
+ }
+ }
+ }
+ assert noPendingWrappersOrConversions();
+ generateTrackingWarnings();
+ }
+
+ private boolean noPendingWrappersOrConversions() {
+ for (DexProgramClass pendingSyntheticClass :
+ appView.getSyntheticItems().getPendingSyntheticClasses()) {
+ assert !isAPIConversionSyntheticType(pendingSyntheticClass.type, wrapperSynthesizor, appView);
+ }
+ return true;
+ }
+
+ public boolean shouldRegisterCallback(ProgramMethod method) {
+ // Any override of a library method can be called by the library.
+ // We duplicate the method to have a vivified type version callable by the library and
+ // a type version callable by the program. We need to add the vivified version to the rootset
+ // as it is actually overriding a library method (after changing the vivified type to the core
+ // library type), but the enqueuer cannot see that.
+ // To avoid too much computation we first look if the method would need to be rewritten if
+ // it would override a library method, then check if it overrides a library method.
+ DexEncodedMethod definition = method.getDefinition();
+ if (definition.isPrivateMethod()
+ || definition.isStatic()
+ || definition.isAbstract()
+ || definition.isLibraryMethodOverride().isFalse()) {
+ return false;
+ }
+ if (!appView.rewritePrefix.hasRewrittenTypeInSignature(definition.getProto(), appView)
+ || appView
+ .options()
+ .desugaredLibraryConfiguration
+ .getEmulateLibraryInterface()
+ .containsKey(method.getHolderType())) {
+ return false;
+ }
+ // In R8 we should be in the enqueuer, therefore we can duplicate a default method and both
+ // methods will be desugared.
+ // In D8, this happens after interface method desugaring, we cannot introduce new default
+ // methods, but we do not need to since this is a library override (invokes will resolve) and
+ // all implementors have been enhanced with a forwarding method which will be duplicated.
+ if (!appView.enableWholeProgramOptimizations()) {
+ if (method.getHolder().isInterface()
+ && method.getDefinition().isDefaultMethod()
+ && (!appView.options().canUseDefaultAndStaticInterfaceMethods()
+ || appView.options().isDesugaredLibraryCompilation())) {
+ return false;
+ }
+ }
+ if (!appView.options().desugaredLibraryConfiguration.supportAllCallbacksFromLibrary
+ && appView.options().isDesugaredLibraryCompilation()) {
+ return false;
+ }
+ return overridesNonFinalLibraryMethod(method);
+ }
+
+ private boolean overridesNonFinalLibraryMethod(ProgramMethod method) {
+ // We look up everywhere to see if there is a supertype/interface implementing the method...
+ DexProgramClass holder = method.getHolder();
+ WorkList<DexType> workList = WorkList.newIdentityWorkList();
+ workList.addIfNotSeen(holder.interfaces.values);
+ boolean foundOverrideToRewrite = false;
+ // There is no methods with desugared types on Object.
+ if (holder.superType != factory.objectType) {
+ workList.addIfNotSeen(holder.superType);
+ }
+ while (workList.hasNext()) {
+ DexType current = workList.next();
+ DexClass dexClass = appView.definitionFor(current);
+ if (dexClass == null) {
+ continue;
+ }
+ workList.addIfNotSeen(dexClass.interfaces.values);
+ if (dexClass.superType != factory.objectType) {
+ workList.addIfNotSeen(dexClass.superType);
+ }
+ if (!dexClass.isLibraryClass() && !appView.options().isDesugaredLibraryCompilation()) {
+ continue;
+ }
+ if (!shouldGenerateCallbacksForEmulateInterfaceAPIs(dexClass)) {
+ continue;
+ }
+ DexEncodedMethod dexEncodedMethod = dexClass.lookupVirtualMethod(method.getReference());
+ if (dexEncodedMethod != null) {
+ // In this case, the object will be wrapped.
+ if (appView.rewritePrefix.hasRewrittenType(dexClass.type, appView)) {
+ return false;
+ }
+ if (dexEncodedMethod.isFinal()) {
+ // We do not introduce overrides of final methods, in this case, the runtime always
+ // execute the default behavior in the final method.
+ return false;
+ }
+ foundOverrideToRewrite = true;
+ }
+ }
+ return foundOverrideToRewrite;
+ }
+
+ private boolean shouldGenerateCallbacksForEmulateInterfaceAPIs(DexClass dexClass) {
+ if (appView.options().desugaredLibraryConfiguration.supportAllCallbacksFromLibrary) {
+ return true;
+ }
+ Map<DexType, DexType> emulateLibraryInterfaces =
+ appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface();
+ return !(emulateLibraryInterfaces.containsKey(dexClass.type)
+ || emulateLibraryInterfaces.containsValue(dexClass.type));
+ }
+
+ private ProgramMethod generateCallbackMethod(
+ DexEncodedMethod originalMethod,
+ DexProgramClass clazz,
+ DesugaredLibraryAPIConverterPostProcessingEventConsumer eventConsumer) {
+ DexMethod methodToInstall =
+ methodWithVivifiedTypeInSignature(originalMethod.getReference(), clazz.type, appView);
+ CfCode cfCode =
+ new APIConverterWrapperCfCodeProvider(
+ appView,
+ originalMethod.getReference(),
+ null,
+ wrapperSynthesizor,
+ clazz.isInterface(),
+ null)
+ .generateCfCode();
+ DexEncodedMethod newMethod =
+ wrapperSynthesizor.newSynthesizedMethod(methodToInstall, originalMethod, cfCode);
+ newMethod.setCode(cfCode, appView);
+ if (originalMethod.isLibraryMethodOverride().isTrue()) {
+ newMethod.setLibraryMethodOverride(OptionalBool.TRUE);
+ }
+ ProgramMethod callback = new ProgramMethod(clazz, newMethod);
+ if (eventConsumer != null) {
+ eventConsumer.acceptAPIConversionCallback(callback);
+ } else {
+ assert appView.enableWholeProgramOptimizations();
+ }
+ return callback;
+ }
+
+ private void generateTrackingWarnings() {
+ generateTrackDesugaredAPIWarnings(trackedCallBackAPIs, "callback ", appView);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java
index b50ac86..ebab39e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java
@@ -4,49 +4,47 @@
package com.android.tools.r8.ir.desugar.desugaredlibrary;
+import com.android.tools.r8.cf.code.CfArrayLoad;
+import com.android.tools.r8.cf.code.CfArrayStore;
+import com.android.tools.r8.cf.code.CfCheckCast;
+import com.android.tools.r8.cf.code.CfConstNumber;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfNewArray;
+import com.android.tools.r8.cf.code.CfReturn;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.DebugLocalInfo;
-import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexClasspathClass;
-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.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.type.Nullability;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeStatic;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterWrapperCfCodeProvider;
-import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.FreshLocalProvider;
+import com.android.tools.r8.ir.desugar.LocalStackAllocator;
+import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
+import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConversionCfCodeProvider;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.StringDiagnostic;
-import com.android.tools.r8.utils.WorkList;
-import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import com.google.common.collect.Sets;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
-import java.util.IdentityHashMap;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.function.Consumer;
+import org.objectweb.asm.Opcodes;
// I convert library calls with desugared parameters/return values so they can work normally.
// In the JSON of the desugared library, one can specify conversions between desugared and
@@ -62,33 +60,33 @@
// DesugarType is only a rewritten type (generated through rewriting of type).
// The type, from the library, may either be rewritten to the desugarType,
// or be a rewritten type (generated through rewriting of vivifiedType).
-public class DesugaredLibraryAPIConverter {
+public class DesugaredLibraryAPIConverter implements CfInstructionDesugaring {
static final String VIVIFIED_PREFIX = "$-vivified-$.";
public static final String DESCRIPTOR_VIVIFIED_PREFIX = "L$-vivified-$/";
private final AppView<?> appView;
private final DexItemFactory factory;
- // For debugging only, allows to assert that synthesized code in R8 have been synthesized in the
- // Enqueuer and not during IR processing.
- private final Mode mode;
+ // This is used to filter out double desugaring on backported methods.
+ private final BackportedMethodRewriter backportedMethodRewriter;
+ private final InterfaceMethodRewriter interfaceMethodRewriter;
+ private final DesugaredLibraryRetargeter retargeter;
+
private final DesugaredLibraryWrapperSynthesizer wrapperSynthesizor;
- private final Map<DexClass, Set<DexEncodedMethod>> callBackMethods = new IdentityHashMap<>();
- private final Map<DexProgramClass, List<DexEncodedMethod>> pendingCallBackMethods =
- new IdentityHashMap<>();
private final Set<DexMethod> trackedCallBackAPIs;
private final Set<DexMethod> trackedAPIs;
- public enum Mode {
- GENERATE_CALLBACKS_AND_WRAPPERS,
- ASSERT_CALLBACKS_AND_WRAPPERS_GENERATED;
- }
-
- public DesugaredLibraryAPIConverter(AppView<?> appView, Mode mode) {
+ public DesugaredLibraryAPIConverter(
+ AppView<?> appView,
+ InterfaceMethodRewriter interfaceMethodRewriter,
+ DesugaredLibraryRetargeter retargeter,
+ BackportedMethodRewriter backportedMethodRewriter) {
this.appView = appView;
this.factory = appView.dexItemFactory();
- this.mode = mode;
- this.wrapperSynthesizor = new DesugaredLibraryWrapperSynthesizer(appView, this);
+ this.interfaceMethodRewriter = interfaceMethodRewriter;
+ this.retargeter = retargeter;
+ this.backportedMethodRewriter = backportedMethodRewriter;
+ this.wrapperSynthesizor = new DesugaredLibraryWrapperSynthesizer(appView);
if (appView.options().testing.trackDesugaredAPIConversions) {
trackedCallBackAPIs = Sets.newConcurrentHashSet();
trackedAPIs = Sets.newConcurrentHashSet();
@@ -98,209 +96,131 @@
}
}
+ @Override
+ public Collection<CfInstruction> desugarInstruction(
+ CfInstruction instruction,
+ FreshLocalProvider freshLocalProvider,
+ LocalStackAllocator localStackAllocator,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context,
+ MethodProcessingContext methodProcessingContext,
+ DexItemFactory dexItemFactory) {
+ if (needsDesugaring(instruction, context)) {
+ assert instruction.isInvoke();
+ return rewriteLibraryInvoke(
+ instruction.asInvoke(),
+ methodProcessingContext,
+ localStackAllocator,
+ eventConsumer,
+ context);
+ }
+ return null;
+ }
+
+ @Override
+ public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
+ if (!instruction.isInvoke()) {
+ return false;
+ }
+ if (isAPIConversionSyntheticType(context.getHolderType(), wrapperSynthesizor, appView)) {
+ return false;
+ }
+ CfInvoke invoke = instruction.asInvoke();
+ return shouldRewriteInvoke(
+ invoke.getMethod(), invoke.getInvokeType(context), invoke.isInterface(), context);
+ }
+
+ static boolean isAPIConversionSyntheticType(
+ DexType type, DesugaredLibraryWrapperSynthesizer wrapperSynthesizor, AppView<?> appView) {
+ return wrapperSynthesizor.isSyntheticWrapper(type)
+ || appView.getSyntheticItems().isSyntheticOfKind(type, SyntheticKind.API_CONVERSION);
+ }
+
public static boolean isVivifiedType(DexType type) {
return type.descriptor.toString().startsWith(DESCRIPTOR_VIVIFIED_PREFIX);
}
- boolean canGenerateWrappersAndCallbacks() {
- return mode == Mode.GENERATE_CALLBACKS_AND_WRAPPERS;
+ private DexClassAndMethod getMethodForDesugaring(
+ DexMethod invokedMethod, boolean isInvokeSuper, boolean isInterface, ProgramMethod context) {
+ // TODO(b/191656218): Use lookupInvokeSpecial instead when this is all to Cf.
+ return isInvokeSuper
+ ? appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context)
+ : appView
+ .appInfoForDesugaring()
+ .resolveMethod(invokedMethod, isInterface)
+ .getResolutionPair();
}
- public void desugar(IRCode code) {
-
- if (wrapperSynthesizor.isSyntheticWrapper(code.method().getHolderType())) {
- return;
- }
-
- if (!canGenerateWrappersAndCallbacks()) {
- assert validateCallbackWasGeneratedInEnqueuer(code.context());
- } else {
- registerCallbackIfRequired(code.context());
- }
-
- ListIterator<BasicBlock> blockIterator = code.listIterator();
- while (blockIterator.hasNext()) {
- BasicBlock block = blockIterator.next();
- InstructionListIterator iterator = block.listIterator(code);
- while (iterator.hasNext()) {
- Instruction instruction = iterator.next();
- if (!instruction.isInvokeMethod()) {
- continue;
- }
- InvokeMethod invokeMethod = instruction.asInvokeMethod();
- DexMethod invokedMethod;
- if (invokeMethod.isInvokeSuper()) {
- DexClassAndMethod result =
- appView
- .appInfoForDesugaring()
- .lookupSuperTarget(invokeMethod.getInvokedMethod(), code.context());
- invokedMethod = result != null ? result.getReference() : null;
- } else {
- // TODO(b/192439456): Make a test to prove resolution is needed here and fix it.
- invokedMethod = invokeMethod.getInvokedMethod();
- }
- // Library methods do not understand desugared types, hence desugared types have to be
- // converted around non desugared library calls for the invoke to resolve.
- if (invokedMethod != null && shouldRewriteInvoke(invokedMethod)) {
- rewriteLibraryInvoke(code, invokeMethod, iterator, blockIterator);
- }
- }
- }
- }
-
- private boolean validateCallbackWasGeneratedInEnqueuer(ProgramMethod method) {
- if (!shouldRegisterCallback(method)) {
- return true;
- }
- DexMethod installedCallback = methodWithVivifiedTypeInSignature(method, appView);
- assert method.getHolder().lookupMethod(installedCallback) != null;
- return true;
- }
-
- public boolean shouldRewriteInvoke(DexMethod invokedMethod) {
- if (appView.rewritePrefix.hasRewrittenType(invokedMethod.holder, appView)
- || invokedMethod.holder.isArrayType()) {
+ // TODO(b/191656218): Consider caching the result.
+ private boolean shouldRewriteInvoke(
+ DexMethod unresolvedInvokedMethod,
+ Type invokeType,
+ boolean isInterface,
+ ProgramMethod context) {
+ DexClassAndMethod invokedMethod =
+ getMethodForDesugaring(
+ unresolvedInvokedMethod, invokeType == Type.SUPER, isInterface, context);
+ if (invokedMethod == null) {
+ // Implies a resolution/look-up failure, we do not convert to keep the runtime error.
return false;
}
- DexClass dexClass = appView.definitionFor(invokedMethod.holder);
+ DexType holderType = invokedMethod.getHolderType();
+ if (appView.rewritePrefix.hasRewrittenType(holderType, appView) || holderType.isArrayType()) {
+ return false;
+ }
+ DexClass dexClass = appView.definitionFor(holderType);
if (dexClass == null || !dexClass.isLibraryClass()) {
return false;
}
- return appView.rewritePrefix.hasRewrittenTypeInSignature(invokedMethod.proto, appView);
- }
-
- public void registerCallbackIfRequired(ProgramMethod method) {
- if (shouldRegisterCallback(method)) {
- registerCallback(method);
- }
- }
-
- public ProgramMethod generateCallbackIfRequired(ProgramMethod method) {
- if (!shouldRegisterCallback(method)) {
- return null;
- }
- if (trackedCallBackAPIs != null) {
- trackedCallBackAPIs.add(method.getReference());
- }
- ProgramMethod callback = generateCallbackMethod(method.getDefinition(), method.getHolder());
- method.getHolder().addVirtualMethod(callback.getDefinition());
- return callback;
- }
-
- public boolean shouldRegisterCallback(ProgramMethod method) {
- // Any override of a library method can be called by the library.
- // We duplicate the method to have a vivified type version callable by the library and
- // a type version callable by the program. We need to add the vivified version to the rootset
- // as it is actually overriding a library method (after changing the vivified type to the core
- // library type), but the enqueuer cannot see that.
- // To avoid too much computation we first look if the method would need to be rewritten if
- // it would override a library method, then check if it overrides a library method.
- DexEncodedMethod definition = method.getDefinition();
- if (definition.isPrivateMethod()
- || definition.isStatic()
- || definition.isLibraryMethodOverride().isFalse()) {
+ if (isEmulatedInterfaceOverride(invokedMethod)) {
return false;
}
- if (!appView.rewritePrefix.hasRewrittenTypeInSignature(definition.getProto(), appView)
- || appView
+ if (isAlreadyDesugared(unresolvedInvokedMethod, invokeType, isInterface, context)) {
+ return false;
+ }
+ return appView.rewritePrefix.hasRewrittenTypeInSignature(invokedMethod.getProto(), appView);
+ }
+
+ // The problem is that a method can resolve into a library method which is not present at runtime,
+ // the code relies in that case on emulated interface dispatch. We should not convert such API.
+ private boolean isEmulatedInterfaceOverride(DexClassAndMethod invokedMethod) {
+ if (interfaceMethodRewriter == null) {
+ return false;
+ }
+ if (!interfaceMethodRewriter.getEmulatedMethods().contains(invokedMethod.getName())) {
+ return false;
+ }
+ DexClassAndMethod interfaceResult =
+ appView
+ .appInfoForDesugaring()
+ .lookupMaximallySpecificMethod(invokedMethod.getHolder(), invokedMethod.getReference());
+ return interfaceResult != null
+ && appView
.options()
.desugaredLibraryConfiguration
.getEmulateLibraryInterface()
- .containsKey(method.getHolderType())) {
- return false;
- }
- // In R8 we should be in the enqueuer, therefore we can duplicate a default method and both
- // methods will be desugared.
- // In D8, this happens after interface method desugaring, we cannot introduce new default
- // methods, but we do not need to since this is a library override (invokes will resolve) and
- // all implementors have been enhanced with a forwarding method which will be duplicated.
- if (!appView.enableWholeProgramOptimizations()) {
- if (method.getHolder().isInterface()
- && method.getDefinition().isDefaultMethod()
- && (!appView.options().canUseDefaultAndStaticInterfaceMethods()
- || appView.options().isDesugaredLibraryCompilation())) {
- return false;
- }
- }
- if (!appView.options().desugaredLibraryConfiguration.supportAllCallbacksFromLibrary
- && appView.options().isDesugaredLibraryCompilation()) {
- return false;
- }
- return overridesNonFinalLibraryMethod(method);
+ .containsKey(interfaceResult.getHolderType());
}
- private boolean overridesNonFinalLibraryMethod(ProgramMethod method) {
- // We look up everywhere to see if there is a supertype/interface implementing the method...
- DexProgramClass holder = method.getHolder();
- WorkList<DexType> workList = WorkList.newIdentityWorkList();
- workList.addIfNotSeen(holder.interfaces.values);
- boolean foundOverrideToRewrite = false;
- // There is no methods with desugared types on Object.
- if (holder.superType != factory.objectType) {
- workList.addIfNotSeen(holder.superType);
- }
- while (workList.hasNext()) {
- DexType current = workList.next();
- DexClass dexClass = appView.definitionFor(current);
- if (dexClass == null) {
- continue;
- }
- workList.addIfNotSeen(dexClass.interfaces.values);
- if (dexClass.superType != factory.objectType) {
- workList.addIfNotSeen(dexClass.superType);
- }
- if (!dexClass.isLibraryClass() && !appView.options().isDesugaredLibraryCompilation()) {
- continue;
- }
- if (!shouldGenerateCallbacksForEmulateInterfaceAPIs(dexClass)) {
- continue;
- }
- DexEncodedMethod dexEncodedMethod = dexClass.lookupVirtualMethod(method.getReference());
- if (dexEncodedMethod != null) {
- // In this case, the object will be wrapped.
- if (appView.rewritePrefix.hasRewrittenType(dexClass.type, appView)) {
- return false;
- }
- if (dexEncodedMethod.isFinal()) {
- // We do not introduce overrides of final methods, in this case, the runtime always
- // execute the default behavior in the final method.
- return false;
- }
- foundOverrideToRewrite = true;
- }
- }
- return foundOverrideToRewrite;
- }
-
- private boolean shouldGenerateCallbacksForEmulateInterfaceAPIs(DexClass dexClass) {
- if (appView.options().desugaredLibraryConfiguration.supportAllCallbacksFromLibrary) {
+ private boolean isAlreadyDesugared(
+ DexMethod unresolvedInvokedMethod,
+ Type invokeType,
+ boolean isInterface,
+ ProgramMethod context) {
+ if (interfaceMethodRewriter != null
+ && interfaceMethodRewriter.needsRewriting(unresolvedInvokedMethod, invokeType, context)) {
return true;
}
- Map<DexType, DexType> emulateLibraryInterfaces =
- appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface();
- return !(emulateLibraryInterfaces.containsKey(dexClass.type)
- || emulateLibraryInterfaces.containsValue(dexClass.type));
- }
-
- private synchronized void registerCallback(ProgramMethod method) {
- if (trackedCallBackAPIs != null) {
- trackedCallBackAPIs.add(method.getReference());
+ if (retargeter != null
+ && retargeter.hasNewInvokeTarget(
+ unresolvedInvokedMethod, isInterface, invokeType == Type.SUPER, context)) {
+ return true;
}
- addCallBackSignature(method);
- }
-
- private synchronized void addCallBackSignature(ProgramMethod method) {
- DexProgramClass holder = method.getHolder();
- DexEncodedMethod definition = method.getDefinition();
- if (callBackMethods.computeIfAbsent(holder, key -> Sets.newIdentityHashSet()).add(definition)) {
- pendingCallBackMethods.computeIfAbsent(holder, key -> new ArrayList<>()).add(definition);
+ if (backportedMethodRewriter != null
+ && backportedMethodRewriter.methodIsBackport(unresolvedInvokedMethod)) {
+ return true;
}
- }
-
- public static DexMethod methodWithVivifiedTypeInSignature(
- ProgramMethod method, AppView<?> appView) {
- return methodWithVivifiedTypeInSignature(
- method.getReference(), method.getHolderType(), appView);
+ return false;
}
public static DexMethod methodWithVivifiedTypeInSignature(
@@ -322,70 +242,20 @@
return appView.dexItemFactory().createMethod(holder, newProto, originalMethod.name);
}
- public void finalizeWrappers(
- DexApplication.Builder<?> builder, IRConverter irConverter, ExecutorService executorService)
- throws ExecutionException {
- // In D8, we generate the wrappers here. In R8, wrappers have already been generated in the
- // enqueuer, so nothing needs to be done.
- if (appView.enableWholeProgramOptimizations()) {
- return;
- }
- SortedProgramMethodSet callbacks = generateCallbackMethods();
- irConverter.processMethodsConcurrently(callbacks, executorService);
- if (appView.options().isDesugaredLibraryCompilation()) {
- wrapperSynthesizor.finalizeWrappersForL8();
- }
- }
-
- public SortedProgramMethodSet generateCallbackMethods() {
- generateTrackingWarnings();
- SortedProgramMethodSet allCallbackMethods = SortedProgramMethodSet.create();
- pendingCallBackMethods.forEach(
- (clazz, callbacks) -> {
- List<DexEncodedMethod> newVirtualMethods = new ArrayList<>();
- callbacks.forEach(
- callback -> {
- ProgramMethod callbackMethod = generateCallbackMethod(callback, clazz);
- newVirtualMethods.add(callbackMethod.getDefinition());
- allCallbackMethods.add(callbackMethod);
- });
- clazz.addVirtualMethods(newVirtualMethods);
- });
- pendingCallBackMethods.clear();
- return allCallbackMethods;
+ public void ensureWrappersForL8(CfInstructionDesugaringEventConsumer eventConsumer) {
+ assert appView.options().isDesugaredLibraryCompilation();
+ wrapperSynthesizor.ensureWrappersForL8(eventConsumer);
}
public void generateTrackingWarnings() {
- if (appView.options().testing.trackDesugaredAPIConversions) {
- generateTrackDesugaredAPIWarnings(trackedAPIs, "");
- generateTrackDesugaredAPIWarnings(trackedCallBackAPIs, "callback ");
- trackedAPIs.clear();
- trackedCallBackAPIs.clear();
+ generateTrackDesugaredAPIWarnings(trackedAPIs, "", appView);
+ }
+
+ static void generateTrackDesugaredAPIWarnings(
+ Set<DexMethod> tracked, String inner, AppView<?> appView) {
+ if (!appView.options().testing.trackDesugaredAPIConversions) {
+ return;
}
- }
-
- public void synthesizeWrappers(Consumer<DexClasspathClass> synthesizedCallback) {
- wrapperSynthesizor.synthesizeWrappersForClasspath(synthesizedCallback);
- }
-
- private ProgramMethod generateCallbackMethod(
- DexEncodedMethod originalMethod, DexProgramClass clazz) {
- DexMethod methodToInstall =
- methodWithVivifiedTypeInSignature(originalMethod.getReference(), clazz.type, appView);
- CfCode cfCode =
- new APIConverterWrapperCfCodeProvider(
- appView, originalMethod.getReference(), null, this, clazz.isInterface())
- .generateCfCode();
- DexEncodedMethod newMethod =
- wrapperSynthesizor.newSynthesizedMethod(methodToInstall, originalMethod, cfCode);
- newMethod.setCode(cfCode, appView);
- if (originalMethod.isLibraryMethodOverride().isTrue()) {
- newMethod.setLibraryMethodOverride(OptionalBool.TRUE);
- }
- return new ProgramMethod(clazz, newMethod);
- }
-
- private void generateTrackDesugaredAPIWarnings(Set<DexMethod> tracked, String inner) {
StringBuilder sb = new StringBuilder();
sb.append("Tracked ").append(inner).append("desugared API conversions: ");
for (DexMethod method : tracked) {
@@ -393,26 +263,7 @@
sb.append(method);
}
appView.options().reporter.warning(new StringDiagnostic(sb.toString()));
- }
-
- public void reportInvalidInvoke(DexType type, DexMethod invokedMethod, String debugString) {
- DexType desugaredType = appView.rewritePrefix.rewrittenType(type, appView);
- StringDiagnostic diagnostic =
- new StringDiagnostic(
- "Invoke to "
- + invokedMethod.holder
- + "#"
- + invokedMethod.name
- + " may not work correctly at runtime (Cannot convert "
- + debugString
- + "type "
- + desugaredType
- + ").");
- if (appView.options().isDesugaredLibraryCompilation()) {
- throw appView.options().reporter.fatalError(diagnostic);
- } else {
- appView.options().reporter.info(diagnostic);
- }
+ tracked.clear();
}
public static DexType vivifiedTypeFor(DexType type, AppView<?> appView) {
@@ -425,204 +276,296 @@
return vivifiedType;
}
- public void registerWrappersForLibraryInvokeIfRequired(DexMethod invokedMethod) {
- if (!shouldRewriteInvoke(invokedMethod)) {
- return;
+ private static DexType invalidType(
+ DexMethod invokedMethod,
+ DexMethod returnConversion,
+ DexMethod[] parameterConversions,
+ AppView<?> appView) {
+ DexMethod convertedMethod =
+ methodWithVivifiedTypeInSignature(invokedMethod, invokedMethod.holder, appView);
+ if (invokedMethod.getReturnType() != convertedMethod.getReturnType()
+ && returnConversion == null) {
+ return invokedMethod.getReturnType();
}
- if (trackedAPIs != null) {
- trackedAPIs.add(invokedMethod);
- }
- DexType returnType = invokedMethod.proto.returnType;
- if (appView.rewritePrefix.hasRewrittenType(returnType, appView) && canConvert(returnType)) {
- registerConversionWrappers(returnType);
- }
- for (DexType argType : invokedMethod.proto.parameters.values) {
- if (appView.rewritePrefix.hasRewrittenType(argType, appView) && canConvert(argType)) {
- registerConversionWrappers(argType);
+ for (int i = 0; i < invokedMethod.getArity(); i++) {
+ if (invokedMethod.getParameter(i) != convertedMethod.getParameter(i)
+ && parameterConversions[i] == null) {
+ return invokedMethod.getParameter(i);
}
}
+ return null;
}
- private void rewriteLibraryInvoke(
- IRCode code,
- InvokeMethod invokeMethod,
- InstructionListIterator iterator,
- ListIterator<BasicBlock> blockIterator) {
- DexMethod invokedMethod = invokeMethod.getInvokedMethod();
- boolean invalidConversion = false;
- if (trackedAPIs != null) {
- trackedAPIs.add(invokedMethod);
+ public static DexMethod getConvertedAPI(
+ DexMethod invokedMethod,
+ DexMethod returnConversion,
+ DexMethod[] parameterConversions,
+ AppView<?> appView) {
+ DexType newReturnType =
+ returnConversion != null ? returnConversion.getParameter(0) : invokedMethod.getReturnType();
+ DexType[] newParameterTypes = new DexType[parameterConversions.length];
+ for (int i = 0; i < parameterConversions.length; i++) {
+ newParameterTypes[i] =
+ parameterConversions[i] != null
+ ? parameterConversions[i].getReturnType()
+ : invokedMethod.getParameter(i);
}
+ DexMethod convertedAPI =
+ appView
+ .dexItemFactory()
+ .createMethod(
+ invokedMethod.holder,
+ appView.dexItemFactory().createProto(newReturnType, newParameterTypes),
+ invokedMethod.name);
+ assert convertedAPI
+ == methodWithVivifiedTypeInSignature(invokedMethod, invokedMethod.holder, appView)
+ || invalidType(invokedMethod, returnConversion, parameterConversions, appView) != null;
+ return convertedAPI;
+ }
- // Create return conversion if required.
- Instruction returnConversion = null;
- DexType newReturnType;
+ private DexMethod computeReturnConversion(
+ DexMethod invokedMethod, DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
DexType returnType = invokedMethod.proto.returnType;
- if (appView.rewritePrefix.hasRewrittenType(returnType, appView)) {
- if (canConvert(returnType)) {
- newReturnType = vivifiedTypeFor(returnType, appView);
- // Return conversion added only if return value is used.
- if (invokeMethod.outValue() != null
- && invokeMethod.outValue().numberOfUsers() + invokeMethod.outValue().numberOfPhiUsers()
- > 0) {
- returnConversion =
- createReturnConversionAndReplaceUses(code, invokeMethod, returnType, newReturnType);
- }
- } else {
- reportInvalidInvoke(returnType, invokeMethod.getInvokedMethod(), "return ");
- invalidConversion = true;
- newReturnType = returnType;
- }
- } else {
- newReturnType = returnType;
+ if (wrapperSynthesizor.shouldConvert(returnType, invokedMethod)) {
+ DexType newReturnType = DesugaredLibraryAPIConverter.vivifiedTypeFor(returnType, appView);
+ return wrapperSynthesizor.ensureConversionMethod(
+ returnType, newReturnType, returnType, eventConsumer);
}
+ return null;
+ }
- // Create parameter conversions if required.
- List<Instruction> parameterConversions = new ArrayList<>();
- List<Value> newInValues = new ArrayList<>();
- if (invokeMethod.isInvokeMethodWithReceiver()) {
- assert !appView.rewritePrefix.hasRewrittenType(invokedMethod.holder, appView);
- newInValues.add(invokeMethod.asInvokeMethodWithReceiver().getReceiver());
- }
- int receiverShift = BooleanUtils.intValue(invokeMethod.isInvokeMethodWithReceiver());
+ private DexMethod[] computeParameterConversions(
+ DexMethod invokedMethod, DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
+ DexMethod[] parameterConversions = new DexMethod[invokedMethod.getArity()];
DexType[] parameters = invokedMethod.proto.parameters.values;
- DexType[] newParameters = parameters.clone();
for (int i = 0; i < parameters.length; i++) {
DexType argType = parameters[i];
- if (appView.rewritePrefix.hasRewrittenType(argType, appView)) {
- if (canConvert(argType)) {
- DexType argVivifiedType = vivifiedTypeFor(argType, appView);
- Value inValue = invokeMethod.inValues().get(i + receiverShift);
- newParameters[i] = argVivifiedType;
- parameterConversions.add(
- createParameterConversion(code, argType, argVivifiedType, inValue));
- newInValues.add(parameterConversions.get(parameterConversions.size() - 1).outValue());
- } else {
- reportInvalidInvoke(argType, invokeMethod.getInvokedMethod(), "parameter ");
- invalidConversion = true;
- newInValues.add(invokeMethod.inValues().get(i + receiverShift));
- }
- } else {
- newInValues.add(invokeMethod.inValues().get(i + receiverShift));
+ if (wrapperSynthesizor.shouldConvert(argType, invokedMethod)) {
+ DexType argVivifiedType = vivifiedTypeFor(argType, appView);
+ parameterConversions[i] =
+ wrapperSynthesizor.ensureConversionMethod(
+ argType, argType, argVivifiedType, eventConsumer);
}
}
+ return parameterConversions;
+ }
- // Patch the invoke with new types and new inValues.
- DexProto newProto = factory.createProto(newReturnType, newParameters);
- DexMethod newDexMethod =
- factory.createMethod(invokedMethod.holder, newProto, invokedMethod.name);
- Invoke newInvokeMethod =
- Invoke.create(
- invokeMethod.getType(),
- newDexMethod,
- newDexMethod.proto,
- invokeMethod.outValue(),
- newInValues);
- assert newDexMethod
- == methodWithVivifiedTypeInSignature(invokedMethod, invokedMethod.holder, appView)
- || invalidConversion;
-
- // Insert and reschedule all instructions.
- iterator.previous();
- for (Instruction parameterConversion : parameterConversions) {
- parameterConversion.setPosition(invokeMethod.getPosition());
- iterator.add(parameterConversion);
+ private Collection<CfInstruction> rewriteLibraryInvoke(
+ CfInvoke invoke,
+ MethodProcessingContext methodProcessingContext,
+ LocalStackAllocator localStackAllocator,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context) {
+ DexMethod invokedMethod = invoke.getMethod();
+ if (trackedAPIs != null) {
+ trackedAPIs.add(invokedMethod);
}
- assert iterator.peekNext() == invokeMethod;
- iterator.next();
- iterator.replaceCurrentInstruction(newInvokeMethod);
+ if (shouldOutlineAPIConversion(invoke, context)) {
+ DexMethod outlinedAPIConversion =
+ createOutlinedAPIConversion(invoke, methodProcessingContext, eventConsumer);
+ return Collections.singletonList(
+ new CfInvoke(Opcodes.INVOKESTATIC, outlinedAPIConversion, false));
+ }
+ return rewriteLibraryInvokeToInlineAPIConversion(
+ invoke, methodProcessingContext, localStackAllocator, eventConsumer);
+ }
+
+ // If the option is set, we try to outline API conversions as much as possible to reduce the
+ // number
+ // of soft verification failures. We cannot outline API conversions through super invokes, to
+ // instance initializers and to non public methods.
+ private boolean shouldOutlineAPIConversion(CfInvoke invoke, ProgramMethod context) {
+ if (invoke.isInvokeSuper(context.getHolderType())) {
+ return false;
+ }
+ if (invoke.getMethod().isInstanceInitializer(appView.dexItemFactory())) {
+ return false;
+ }
+ DexClassAndMethod methodForDesugaring =
+ getMethodForDesugaring(invoke.getMethod(), false, invoke.isInterface(), context);
+ assert methodForDesugaring != null;
+ return methodForDesugaring.getAccessFlags().isPublic();
+ }
+
+ private Collection<CfInstruction> rewriteLibraryInvokeToInlineAPIConversion(
+ CfInvoke invoke,
+ MethodProcessingContext methodProcessingContext,
+ LocalStackAllocator localStackAllocator,
+ CfInstructionDesugaringEventConsumer eventConsumer) {
+
+ DexMethod invokedMethod = invoke.getMethod();
+ DexMethod returnConversion = computeReturnConversion(invokedMethod, eventConsumer);
+ DexMethod[] parameterConversions = computeParameterConversions(invokedMethod, eventConsumer);
+
+ // If only the last 2 parameters require conversion, we do everything inlined.
+ // If other parameters require conversion, we outline the parameter conversion but keep the API
+ // call inlined.
+ // The returned value is always converted inlined.
+ boolean requireOutlinedParameterConversion = false;
+ for (int i = 0; i < parameterConversions.length - 2; i++) {
+ requireOutlinedParameterConversion |= parameterConversions[i] != null;
+ }
+
+ ArrayList<CfInstruction> cfInstructions = new ArrayList<>();
+ if (requireOutlinedParameterConversion) {
+ addOutlineParameterConversionInstructions(
+ parameterConversions,
+ cfInstructions,
+ methodProcessingContext,
+ invokedMethod,
+ localStackAllocator,
+ eventConsumer);
+ } else {
+ addInlineParameterConversionInstructions(parameterConversions, cfInstructions);
+ }
+
+ DexMethod convertedMethod =
+ getConvertedAPI(invokedMethod, returnConversion, parameterConversions, appView);
+ cfInstructions.add(new CfInvoke(invoke.getOpcode(), convertedMethod, invoke.isInterface()));
+
if (returnConversion != null) {
- returnConversion.setPosition(invokeMethod.getPosition());
- iterator.add(returnConversion);
+ cfInstructions.add(new CfInvoke(Opcodes.INVOKESTATIC, returnConversion, false));
}
- // If the invoke is in a try-catch, since all conversions can throw, the basic block needs
- // to be split in between each invoke...
- if (newInvokeMethod.getBlock().hasCatchHandlers()) {
- splitIfCatchHandlers(code, newInvokeMethod.getBlock(), blockIterator);
- }
+ return cfInstructions;
}
- private void splitIfCatchHandlers(
- IRCode code,
- BasicBlock blockWithIncorrectThrowingInstructions,
- ListIterator<BasicBlock> blockIterator) {
- InstructionListIterator instructionsIterator =
- blockWithIncorrectThrowingInstructions.listIterator(code);
- BasicBlock currentBlock = blockWithIncorrectThrowingInstructions;
- while (currentBlock != null && instructionsIterator.hasNext()) {
- Instruction throwingInstruction =
- instructionsIterator.nextUntil(Instruction::instructionTypeCanThrow);
- BasicBlock nextBlock;
- if (throwingInstruction != null) {
- nextBlock = instructionsIterator.split(code, blockIterator);
- // Back up to before the split before inserting catch handlers.
- blockIterator.previous();
- nextBlock.copyCatchHandlers(code, blockIterator, currentBlock, appView.options());
- BasicBlock b = blockIterator.next();
- assert b == nextBlock;
- // Switch iteration to the split block.
- instructionsIterator = nextBlock.listIterator(code);
- currentBlock = nextBlock;
+ // The parameters are converted and returned in an array of converted parameters. The parameter
+ // array then needs to be unwrapped at the call site.
+ private void addOutlineParameterConversionInstructions(
+ DexMethod[] parameterConversions,
+ ArrayList<CfInstruction> cfInstructions,
+ MethodProcessingContext methodProcessingContext,
+ DexMethod invokedMethod,
+ LocalStackAllocator localStackAllocator,
+ CfInstructionDesugaringEventConsumer eventConsumer) {
+ localStackAllocator.allocateLocalStack(4);
+ DexProto newProto =
+ appView
+ .dexItemFactory()
+ .createProto(
+ appView.dexItemFactory().objectArrayType, invokedMethod.getParameters().values);
+ ProgramMethod parameterConversion =
+ appView
+ .getSyntheticItems()
+ .createMethod(
+ SyntheticKind.API_CONVERSION_PARAMETERS,
+ methodProcessingContext.createUniqueContext(),
+ appView,
+ builder ->
+ builder
+ .setProto(newProto)
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setCode(
+ methodSignature ->
+ computeParameterConversionCfCode(
+ methodSignature.holder, invokedMethod, parameterConversions)));
+ eventConsumer.acceptAPIConversion(parameterConversion);
+ cfInstructions.add(
+ new CfInvoke(Opcodes.INVOKESTATIC, parameterConversion.getReference(), false));
+ for (int i = 0; i < parameterConversions.length; i++) {
+ cfInstructions.add(new CfStackInstruction(Opcode.Dup));
+ cfInstructions.add(new CfConstNumber(i, ValueType.INT));
+ DexType parameterType =
+ parameterConversions[i] != null
+ ? parameterConversions[i].getReturnType()
+ : invokedMethod.getParameter(i);
+ cfInstructions.add(new CfArrayLoad(MemberType.OBJECT));
+ if (parameterType.isPrimitiveType()) {
+ cfInstructions.add(new CfCheckCast(factory.getBoxedForPrimitiveType(parameterType)));
+ DexMethod method = appView.dexItemFactory().getUnboxPrimitiveMethod(parameterType);
+ cfInstructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, method, false));
} else {
- assert !instructionsIterator.hasNext();
- instructionsIterator = null;
- currentBlock = null;
+ cfInstructions.add(new CfCheckCast(parameterType));
}
+ cfInstructions.add(new CfStackInstruction(Opcode.Swap));
+ }
+ cfInstructions.add(new CfStackInstruction(Opcode.Pop));
+ }
+
+ private CfCode computeParameterConversionCfCode(
+ DexType holder, DexMethod invokedMethod, DexMethod[] parameterConversions) {
+ ArrayList<CfInstruction> cfInstructions = new ArrayList<>();
+ cfInstructions.add(new CfConstNumber(parameterConversions.length, ValueType.INT));
+ cfInstructions.add(new CfNewArray(factory.objectArrayType));
+ int stackIndex = 0;
+ for (int i = 0; i < invokedMethod.getArity(); i++) {
+ cfInstructions.add(new CfStackInstruction(Opcode.Dup));
+ cfInstructions.add(new CfConstNumber(i, ValueType.INT));
+ DexType param = invokedMethod.getParameter(i);
+ cfInstructions.add(new CfLoad(ValueType.fromDexType(param), stackIndex));
+ if (parameterConversions[i] != null) {
+ cfInstructions.add(new CfInvoke(Opcodes.INVOKESTATIC, parameterConversions[i], false));
+ }
+ if (param.isPrimitiveType()) {
+ DexMethod method = appView.dexItemFactory().getBoxPrimitiveMethod(param);
+ cfInstructions.add(new CfInvoke(Opcodes.INVOKESTATIC, method, false));
+ }
+ cfInstructions.add(new CfArrayStore(MemberType.OBJECT));
+ if (param == appView.dexItemFactory().longType
+ || param == appView.dexItemFactory().doubleType) {
+ stackIndex++;
+ }
+ stackIndex++;
+ }
+ cfInstructions.add(new CfReturn(ValueType.OBJECT));
+ return new CfCode(
+ holder,
+ invokedMethod.getParameters().size() + 4,
+ invokedMethod.getParameters().size(),
+ cfInstructions);
+ }
+
+ private void addInlineParameterConversionInstructions(
+ DexMethod[] parameterConversions, ArrayList<CfInstruction> cfInstructions) {
+ if (parameterConversions.length > 0
+ && parameterConversions[parameterConversions.length - 1] != null) {
+ cfInstructions.add(
+ new CfInvoke(
+ Opcodes.INVOKESTATIC, parameterConversions[parameterConversions.length - 1], false));
+ }
+ if (parameterConversions.length > 1
+ && parameterConversions[parameterConversions.length - 2] != null) {
+ cfInstructions.add(new CfStackInstruction(Opcode.Swap));
+ cfInstructions.add(
+ new CfInvoke(
+ Opcodes.INVOKESTATIC, parameterConversions[parameterConversions.length - 2], false));
+ cfInstructions.add(new CfStackInstruction(Opcode.Swap));
}
}
- private Instruction createParameterConversion(
- IRCode code, DexType argType, DexType argVivifiedType, Value inValue) {
- DexMethod conversionMethod = ensureConversionMethod(argType, argType, argVivifiedType);
- // The value is null only if the input is null.
- Value convertedValue =
- createConversionValue(code, inValue.getType().nullability(), argVivifiedType, null);
- return new InvokeStatic(conversionMethod, convertedValue, Collections.singletonList(inValue));
- }
-
- private Instruction createReturnConversionAndReplaceUses(
- IRCode code, InvokeMethod invokeMethod, DexType returnType, DexType returnVivifiedType) {
- DexMethod conversionMethod = ensureConversionMethod(returnType, returnVivifiedType, returnType);
- Value outValue = invokeMethod.outValue();
- Value convertedValue =
- createConversionValue(code, Nullability.maybeNull(), returnType, outValue.getLocalInfo());
- outValue.replaceUsers(convertedValue);
- // The only user of out value is now the new invoke static, so no type propagation is required.
- outValue.setType(
- TypeElement.fromDexType(returnVivifiedType, outValue.getType().nullability(), appView));
- return new InvokeStatic(conversionMethod, convertedValue, Collections.singletonList(outValue));
- }
-
- private void registerConversionWrappers(DexType type) {
- if (appView.options().desugaredLibraryConfiguration.getCustomConversions().get(type) == null) {
- wrapperSynthesizor.registerWrapper(type);
- }
- }
-
- public DexMethod ensureConversionMethod(DexType type, DexType srcType, DexType destType) {
- // ConversionType holds the methods "rewrittenType convert(type)" and the other way around.
- // But everything is going to be rewritten, so we need to use vivifiedType and type".
- DexType conversionHolder =
- appView.options().desugaredLibraryConfiguration.getCustomConversions().get(type);
- if (conversionHolder == null) {
- conversionHolder =
- type == srcType
- ? wrapperSynthesizor.ensureTypeWrapper(type)
- : wrapperSynthesizor.ensureVivifiedTypeWrapper(type);
- }
- assert conversionHolder != null;
- return factory.createMethod(
- conversionHolder, factory.createProto(destType, srcType), factory.convertMethodName);
- }
-
- private Value createConversionValue(
- IRCode code, Nullability nullability, DexType valueType, DebugLocalInfo localInfo) {
- return code.createValue(TypeElement.fromDexType(valueType, nullability, appView), localInfo);
- }
-
- public boolean canConvert(DexType type) {
- return appView.options().desugaredLibraryConfiguration.getCustomConversions().containsKey(type)
- || wrapperSynthesizor.canGenerateWrapper(type);
+ private DexMethod createOutlinedAPIConversion(
+ CfInvoke invoke,
+ MethodProcessingContext methodProcessingContext,
+ CfInstructionDesugaringEventConsumer eventConsumer) {
+ DexMethod invokedMethod = invoke.getMethod();
+ DexProto newProto =
+ invoke.isInvokeStatic()
+ ? invokedMethod.proto
+ : factory.prependTypeToProto(invokedMethod.getHolderType(), invokedMethod.getProto());
+ DexMethod returnConversion = computeReturnConversion(invokedMethod, eventConsumer);
+ DexMethod[] parameterConversions = computeParameterConversions(invokedMethod, eventConsumer);
+ ProgramMethod outline =
+ appView
+ .getSyntheticItems()
+ .createMethod(
+ SyntheticKind.API_CONVERSION,
+ methodProcessingContext.createUniqueContext(),
+ appView,
+ builder ->
+ builder
+ .setProto(newProto)
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setCode(
+ methodSignature ->
+ new APIConversionCfCodeProvider(
+ appView,
+ methodSignature.holder,
+ invoke,
+ returnConversion,
+ parameterConversions)
+ .generateCfCode()));
+ eventConsumer.acceptAPIConversion(outline);
+ return outline.getReference();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverterEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverterEventConsumer.java
new file mode 100644
index 0000000..7af64bb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverterEventConsumer.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.desugaredlibrary;
+
+import com.android.tools.r8.graph.DexClasspathClass;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramMethod;
+
+public interface DesugaredLibraryAPIConverterEventConsumer {
+
+ void acceptWrapperProgramClass(DexProgramClass clazz);
+
+ void acceptWrapperClasspathClass(DexClasspathClass clazz);
+
+ void acceptAPIConversion(ProgramMethod method);
+
+ interface DesugaredLibraryAPIConverterPostProcessingEventConsumer {
+
+ void acceptAPIConversionCallback(ProgramMethod method);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeter.java
index 43b26e1..a4c9b12 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeter.java
@@ -17,8 +17,8 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -198,7 +198,7 @@
return NO_REWRITING;
}
// We need to force resolution, even on d8, to know if the invoke has to be rewritten.
- ResolutionResult resolutionResult =
+ MethodResolutionResult resolutionResult =
appView.appInfoForDesugaring().resolveMethod(invokedMethod, isInterface);
if (resolutionResult.isFailedResolution()) {
return NO_REWRITING;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterPostProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterPostProcessor.java
index dac3079..64d3cc6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterPostProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterPostProcessor.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
import com.google.common.collect.Maps;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -44,16 +45,19 @@
@Override
public void postProcessingDesugaring(
- CfPostProcessingDesugaringEventConsumer eventConsumer, ExecutorService executorService)
+ Collection<DexProgramClass> programClasses,
+ CfPostProcessingDesugaringEventConsumer eventConsumer,
+ ExecutorService executorService)
throws ExecutionException {
if (appView.options().isDesugaredLibraryCompilation()) {
ensureEmulatedDispatchMethodsSynthesized(eventConsumer);
} else {
- ensureInterfacesAndForwardingMethodsSynthesized(eventConsumer);
+ ensureInterfacesAndForwardingMethodsSynthesized(programClasses, eventConsumer);
}
}
private void ensureInterfacesAndForwardingMethodsSynthesized(
+ Collection<DexProgramClass> programClasses,
DesugaredLibraryRetargeterPostProcessingEventConsumer eventConsumer) {
assert !appView.options().isDesugaredLibraryCompilation();
Map<DexType, List<DexClassAndMethod>> map = Maps.newIdentityHashMap();
@@ -61,7 +65,7 @@
map.putIfAbsent(emulatedDispatchMethod.getHolderType(), new ArrayList<>(1));
map.get(emulatedDispatchMethod.getHolderType()).add(emulatedDispatchMethod);
}
- for (DexProgramClass clazz : appView.appInfo().classes()) {
+ for (DexProgramClass clazz : programClasses) {
if (clazz.superType == null) {
assert clazz.type == appView.dexItemFactory().objectType : clazz.type.toSourceString();
continue;
@@ -110,6 +114,9 @@
// methods.
// We cannot use the ClassProcessor since this applies up to 26, while the ClassProcessor
// applies up to 24.
+ if (appView.isAlreadyLibraryDesugared(clazz)) {
+ return;
+ }
for (DexClassAndMethod method : methods) {
DexClass newInterface =
syntheticHelper.ensureEmulatedInterfaceDispatchMethod(method, eventConsumer);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterSyntheticHelper.java
index 1f8322f..02eb59b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterSyntheticHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterSyntheticHelper.java
@@ -150,6 +150,7 @@
.setCode(
methodSig ->
new EmulateInterfaceSyntheticCfCodeProvider(
+ methodSig.getHolderType(),
emulatedDispatchMethod.getHolderType(),
desugarMethod,
itfMethod,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java
index 6c1e889..2605672 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java
@@ -34,16 +34,16 @@
import com.android.tools.r8.synthesis.SyntheticClassBuilder;
import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -93,19 +93,13 @@
public class DesugaredLibraryWrapperSynthesizer {
private final AppView<?> appView;
- private final Set<DexType> wrappersToGenerate = Sets.newConcurrentHashSet();
- // The invalidWrappers are wrappers with incorrect behavior because of final methods that could
- // not be overridden. Such wrappers are awful because the runtime behavior is undefined and does
- // not raise explicit errors. So we register them here and conversion methods for such wrappers
- // raise a runtime exception instead of generating the wrapper.
- private final Set<DexType> invalidWrappers = Sets.newConcurrentHashSet();
private final DexItemFactory factory;
- private final DesugaredLibraryAPIConverter converter;
+ private final ConcurrentHashMap<DexType, List<DexEncodedMethod>> allImplementedMethodsCache =
+ new ConcurrentHashMap<>();
- DesugaredLibraryWrapperSynthesizer(AppView<?> appView, DesugaredLibraryAPIConverter converter) {
+ DesugaredLibraryWrapperSynthesizer(AppView<?> appView) {
this.appView = appView;
this.factory = appView.dexItemFactory();
- this.converter = converter;
}
public boolean isSyntheticWrapper(DexType type) {
@@ -113,21 +107,72 @@
|| appView.getSyntheticItems().isSyntheticOfKind(type, SyntheticKind.VIVIFIED_WRAPPER);
}
- boolean canGenerateWrapper(DexType type) {
+ public boolean shouldConvert(DexType type, DexMethod method) {
+ if (!appView.rewritePrefix.hasRewrittenType(type, appView)) {
+ return false;
+ }
+ if (canConvert(type)) {
+ return true;
+ }
+ reportInvalidInvoke(type, method);
+ return false;
+ }
+
+ public DexMethod ensureConversionMethod(
+ DexType type,
+ DexType srcType,
+ DexType destType,
+ DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
+ // ConversionType holds the methods "rewrittenType convert(type)" and the other way around.
+ // But everything is going to be rewritten, so we need to use vivifiedType and type".
+ DexType conversionHolder =
+ appView.options().desugaredLibraryConfiguration.getCustomConversions().get(type);
+ if (conversionHolder == null) {
+ conversionHolder =
+ type == srcType
+ ? ensureTypeWrapper(type, eventConsumer)
+ : ensureVivifiedTypeWrapper(type, eventConsumer);
+ }
+ assert conversionHolder != null;
+ return factory.createMethod(
+ conversionHolder, factory.createProto(destType, srcType), factory.convertMethodName);
+ }
+
+ private boolean canConvert(DexType type) {
+ return appView.options().desugaredLibraryConfiguration.getCustomConversions().containsKey(type)
+ || canGenerateWrapper(type);
+ }
+
+ private void reportInvalidInvoke(DexType type, DexMethod invokedMethod) {
+ DexType desugaredType = appView.rewritePrefix.rewrittenType(type, appView);
+ StringDiagnostic diagnostic =
+ new StringDiagnostic(
+ "Invoke to "
+ + invokedMethod.holder
+ + "#"
+ + invokedMethod.name
+ + " may not work correctly at runtime (Cannot convert type "
+ + desugaredType
+ + ").");
+ if (appView.options().isDesugaredLibraryCompilation()) {
+ throw appView.options().reporter.fatalError(diagnostic);
+ } else {
+ appView.options().reporter.info(diagnostic);
+ }
+ }
+
+ private boolean canGenerateWrapper(DexType type) {
return appView.options().desugaredLibraryConfiguration.getWrapperConversions().contains(type);
}
- DexType ensureTypeWrapper(DexType type) {
- return ensureWrappers(type).getWrapper().type;
+ private DexType ensureTypeWrapper(
+ DexType type, DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
+ return ensureWrappers(type, eventConsumer).getWrapper().type;
}
- DexType ensureVivifiedTypeWrapper(DexType type) {
- return ensureWrappers(type).getVivifiedWrapper().type;
- }
-
- public void registerWrapper(DexType type) {
- wrappersToGenerate.add(type);
- assert getValidClassToWrap(type) != null;
+ private DexType ensureVivifiedTypeWrapper(
+ DexType type, DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
+ return ensureWrappers(type, eventConsumer).getVivifiedWrapper().type;
}
private DexClass getValidClassToWrap(DexType type) {
@@ -161,13 +206,17 @@
}
}
- private Wrappers ensureWrappers(DexType type) {
+ private Wrappers ensureWrappers(
+ DexType type, DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
assert canGenerateWrapper(type) : type;
DexClass dexClass = getValidClassToWrap(type);
- return ensureWrappers(dexClass, ignored -> {});
+ return ensureWrappers(dexClass, ignored -> {}, eventConsumer);
}
- private Wrappers ensureWrappers(DexClass context, Consumer<DexClasspathClass> creationCallback) {
+ private Wrappers ensureWrappers(
+ DexClass context,
+ Consumer<DexClasspathClass> creationCallback,
+ DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
DexType type = context.type;
DexClass wrapper;
DexClass vivifiedWrapper;
@@ -180,15 +229,20 @@
vivifiedTypeFor(type),
type,
programContext,
- wrapperField -> synthesizeVirtualMethodsForTypeWrapper(programContext, wrapperField));
+ eventConsumer,
+ wrapperField ->
+ synthesizeVirtualMethodsForTypeWrapper(
+ programContext, wrapperField, eventConsumer));
vivifiedWrapper =
ensureProgramWrapper(
SyntheticKind.VIVIFIED_WRAPPER,
type,
vivifiedTypeFor(type),
programContext,
+ eventConsumer,
wrapperField ->
- synthesizeVirtualMethodsForVivifiedTypeWrapper(programContext, wrapperField));
+ synthesizeVirtualMethodsForVivifiedTypeWrapper(
+ programContext, wrapperField, eventConsumer));
DexField wrapperField = getWrapperUniqueField(wrapper);
DexField vivifiedWrapperField = getWrapperUniqueField(vivifiedWrapper);
ensureProgramConversionMethod(
@@ -205,7 +259,9 @@
type,
classpathOrLibraryContext,
creationCallback,
- wrapperField -> synthesizeVirtualMethodsForTypeWrapper(context, wrapperField));
+ eventConsumer,
+ wrapperField ->
+ synthesizeVirtualMethodsForTypeWrapper(context, wrapperField, eventConsumer));
vivifiedWrapper =
ensureClasspathWrapper(
SyntheticKind.VIVIFIED_WRAPPER,
@@ -213,8 +269,10 @@
vivifiedTypeFor(type),
classpathOrLibraryContext,
creationCallback,
+ eventConsumer,
wrapperField ->
- synthesizeVirtualMethodsForVivifiedTypeWrapper(context, wrapperField));
+ synthesizeVirtualMethodsForVivifiedTypeWrapper(
+ context, wrapperField, eventConsumer));
DexField wrapperField = getWrapperUniqueField(wrapper);
DexField vivifiedWrapperField = getWrapperUniqueField(vivifiedWrapper);
ensureClasspathConversionMethod(
@@ -242,6 +300,7 @@
DexType wrappingType,
DexType wrappedType,
DexProgramClass programContext,
+ DesugaredLibraryAPIConverterEventConsumer eventConsumer,
Function<DexEncodedField, DexEncodedMethod[]> virtualMethodProvider) {
return appView
.getSyntheticItems()
@@ -252,9 +311,13 @@
builder -> buildWrapper(wrappingType, wrappedType, programContext, builder),
// The creation of virtual methods may require new wrappers, this needs to happen
// once the wrapper is created to avoid infinite recursion.
- wrapper ->
- wrapper.setVirtualMethods(
- virtualMethodProvider.apply(getWrapperUniqueEncodedField(wrapper))));
+ wrapper -> {
+ if (eventConsumer != null) {
+ eventConsumer.acceptWrapperProgramClass(wrapper);
+ }
+ wrapper.setVirtualMethods(
+ virtualMethodProvider.apply(getWrapperUniqueEncodedField(wrapper)));
+ });
}
private DexClasspathClass ensureClasspathWrapper(
@@ -263,6 +326,7 @@
DexType wrappedType,
ClasspathOrLibraryClass classpathOrLibraryContext,
Consumer<DexClasspathClass> creationCallback,
+ DesugaredLibraryAPIConverterEventConsumer eventConsumer,
Function<DexEncodedField, DexEncodedMethod[]> virtualMethodProvider) {
return appView
.getSyntheticItems()
@@ -276,6 +340,9 @@
// The creation of virtual methods may require new wrappers, this needs to happen
// once the wrapper is created to avoid infinite recursion.
wrapper -> {
+ if (eventConsumer != null) {
+ eventConsumer.acceptWrapperClasspathClass(wrapper);
+ }
wrapper.setVirtualMethods(
virtualMethodProvider.apply(getWrapperUniqueEncodedField(wrapper)));
creationCallback.accept(wrapper);
@@ -297,8 +364,7 @@
appView,
ignored -> {},
methodBuilder ->
- buildConversionMethod(
- methodBuilder, wrapperField, reverseWrapperField, context.type));
+ buildConversionMethod(methodBuilder, wrapperField, reverseWrapperField, context));
}
private DexClassAndMethod ensureClasspathConversionMethod(
@@ -317,22 +383,26 @@
ignored -> {},
methodBuilder ->
buildConversionMethod(
- methodBuilder, wrapperField, reverseWrapperField, context.getType()));
+ methodBuilder, wrapperField, reverseWrapperField, context.asDexClass()));
+ }
+
+ private boolean isInvalidWrapper(DexClass clazz) {
+ return Iterables.any(allImplementedMethods(clazz), DexEncodedMethod::isFinal);
}
private void buildConversionMethod(
SyntheticMethodBuilder methodBuilder,
DexField wrapperField,
DexField reverseWrapperField,
- DexType reportingType) {
+ DexClass context) {
CfCode cfCode;
- if (invalidWrappers.contains(wrapperField.holder)) {
+ if (isInvalidWrapper(context)) {
cfCode =
new APIConverterThrowRuntimeExceptionCfCodeProvider(
appView,
factory.createString(
"Unsupported conversion for "
- + reportingType
+ + context.type
+ ". See compilation time warnings for more details."),
wrapperField.holder)
.generateCfCode();
@@ -382,7 +452,9 @@
}
private DexEncodedMethod[] synthesizeVirtualMethodsForVivifiedTypeWrapper(
- DexClass dexClass, DexEncodedField wrapperField) {
+ DexClass dexClass,
+ DexEncodedField wrapperField,
+ DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
List<DexEncodedMethod> dexMethods = allImplementedMethods(dexClass);
List<DexEncodedMethod> generatedMethods = new ArrayList<>();
// Each method should use only types in their signature, but each method the wrapper forwards
@@ -415,13 +487,17 @@
dexEncodedMethod.getReference().name);
CfCode cfCode;
if (dexEncodedMethod.isFinal()) {
- invalidWrappers.add(wrapperField.getHolderType());
finalMethods.add(dexEncodedMethod.getReference());
continue;
} else {
cfCode =
new APIConverterVivifiedWrapperCfCodeProvider(
- appView, methodToInstall, wrapperField.getReference(), converter, isInterface)
+ appView,
+ methodToInstall,
+ wrapperField.getReference(),
+ this,
+ isInterface,
+ eventConsumer)
.generateCfCode();
}
DexEncodedMethod newDexEncodedMethod =
@@ -432,7 +508,9 @@
}
private DexEncodedMethod[] synthesizeVirtualMethodsForTypeWrapper(
- DexClass dexClass, DexEncodedField wrapperField) {
+ DexClass dexClass,
+ DexEncodedField wrapperField,
+ DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
List<DexEncodedMethod> dexMethods = allImplementedMethods(dexClass);
List<DexEncodedMethod> generatedMethods = new ArrayList<>();
// Each method should use only vivified types in their signature, but each method the wrapper
@@ -455,7 +533,6 @@
dexEncodedMethod.getReference(), wrapperField.getHolderType(), appView);
CfCode cfCode;
if (dexEncodedMethod.isFinal()) {
- invalidWrappers.add(wrapperField.getHolderType());
finalMethods.add(dexEncodedMethod.getReference());
continue;
} else {
@@ -464,8 +541,9 @@
appView,
dexEncodedMethod.getReference(),
wrapperField.getReference(),
- converter,
- isInterface)
+ this,
+ isInterface,
+ eventConsumer)
.generateCfCode();
}
DexEncodedMethod newDexEncodedMethod =
@@ -522,7 +600,12 @@
true);
}
- private List<DexEncodedMethod> allImplementedMethods(DexClass libraryClass) {
+ private List<DexEncodedMethod> allImplementedMethods(DexClass clazz) {
+ return allImplementedMethodsCache.computeIfAbsent(
+ clazz.type, type -> internalAllImplementedMethods(clazz));
+ }
+
+ private List<DexEncodedMethod> internalAllImplementedMethods(DexClass libraryClass) {
LinkedList<DexClass> workList = new LinkedList<>();
List<DexEncodedMethod> implementedMethods = new ArrayList<>();
workList.add(libraryClass);
@@ -574,7 +657,7 @@
field, fieldAccessFlags, FieldTypeSignature.noSignature(), DexAnnotationSet.empty(), null);
}
- void finalizeWrappersForL8() {
+ void ensureWrappersForL8(DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
DesugaredLibraryConfiguration conf = appView.options().desugaredLibraryConfiguration;
for (DexType type : conf.getWrapperConversions()) {
assert !conf.getCustomConversions().containsKey(type);
@@ -582,24 +665,7 @@
// In broken set-ups we can end up having a json files containing wrappers of non desugared
// classes. Such wrappers are not required since the class won't be rewritten.
if (validClassToWrap.isProgramClass()) {
- ensureWrappers(validClassToWrap, ignored -> {});
- }
- }
- }
-
- void synthesizeWrappersForClasspath(Consumer<DexClasspathClass> synthesizedCallback) {
- BooleanBox changed = new BooleanBox(true);
- while (changed.get()) {
- changed.set(false);
- Set<DexType> copy = new HashSet<>(wrappersToGenerate);
- for (DexType type : copy) {
- DexClass validClassToWrap = getValidClassToWrap(type);
- ensureWrappers(
- validClassToWrap,
- classpathWrapper -> {
- changed.set(true);
- synthesizedCallback.accept(classpathWrapper);
- });
+ ensureWrappers(validClassToWrap, ignored -> {}, eventConsumer);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index 14e5fea..90a7b35 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -27,8 +27,8 @@
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.LibraryMethod;
import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ParameterAnnotationsList;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.BooleanUtils;
@@ -309,8 +309,8 @@
return appView.appInfo().definitionForDesugarDependency(directSubClass, type);
}
- public void reportMissingType(DexType missingType, InterfaceMethodRewriter rewriter) {
- rewriter.warnMissingInterface(closestProgramSubClass, closestProgramSubClass, missingType);
+ public void reportMissingType(DexType missingType, InterfaceDesugaringSyntheticHelper helper) {
+ helper.warnMissingInterface(closestProgramSubClass, closestProgramSubClass, missingType);
}
}
@@ -334,14 +334,14 @@
}
@Override
- public void reportMissingType(DexType missingType, InterfaceMethodRewriter rewriter) {
+ public void reportMissingType(DexType missingType, InterfaceDesugaringSyntheticHelper helper) {
// Ignore missing types in the library.
}
}
private final AppView<?> appView;
private final DexItemFactory dexItemFactory;
- private final InterfaceMethodRewriter rewriter;
+ private final InterfaceDesugaringSyntheticHelper helper;
private final MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
private final boolean needsLibraryInfo;
@@ -358,10 +358,10 @@
private final Map<DexProgramClass, ProgramMethodSet> newSyntheticMethods =
new ConcurrentHashMap<>();
- ClassProcessor(AppView<?> appView, InterfaceMethodRewriter rewriter) {
+ ClassProcessor(AppView<?> appView) {
this.appView = appView;
this.dexItemFactory = appView.dexItemFactory();
- this.rewriter = rewriter;
+ this.helper = new InterfaceDesugaringSyntheticHelper(appView);
needsLibraryInfo =
!appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface().isEmpty()
|| !appView
@@ -402,9 +402,9 @@
private SignaturesInfo computeInterfaceInfo(DexClass iface, SignaturesInfo interfaceInfo) {
assert iface.isInterface();
assert iface.superType == dexItemFactory.objectType;
- assert !rewriter.isEmulatedInterface(iface.type);
+ assert !helper.isEmulatedInterface(iface.type);
// Add non-library default methods as well as those for desugared library classes.
- if (!iface.isLibraryClass() || (needsLibraryInfo() && rewriter.isInDesugaredLibrary(iface))) {
+ if (!iface.isLibraryClass() || (needsLibraryInfo() && helper.isInDesugaredLibrary(iface))) {
MethodSignatures signatures = getDefaultMethods(iface);
interfaceInfo = interfaceInfo.withSignatures(signatures);
}
@@ -415,7 +415,7 @@
DexClass iface, SignaturesInfo interfaceInfo) {
assert iface.isInterface();
assert iface.superType == dexItemFactory.objectType;
- assert rewriter.isEmulatedInterface(iface.type);
+ assert helper.isEmulatedInterface(iface.type);
assert needsLibraryInfo();
MethodSignatures signatures = getDefaultMethods(iface);
EmulatedInterfaceInfo emulatedInterfaceInfo =
@@ -548,7 +548,7 @@
extraInterfaceSignatures.put(
type,
new GenericSignature.ClassTypeSignature(
- rewriter.getEmulatedInterface(type), signature.typeArguments()));
+ helper.getEmulatedInterface(type), signature.typeArguments()));
}
collectEmulatedInterfacesWithPropagatedTypeArguments(
type, signature.typeArguments(), emulatesInterfaces, extraInterfaceSignatures);
@@ -558,8 +558,7 @@
(type) -> {
if (emulatesInterfaces.contains(type)) {
extraInterfaceSignatures.put(
- type,
- new GenericSignature.ClassTypeSignature(rewriter.getEmulatedInterface(type)));
+ type, new GenericSignature.ClassTypeSignature(helper.getEmulatedInterface(type)));
}
collectEmulatedInterfacesWithPropagatedTypeArguments(
type, null, emulatesInterfaces, extraInterfaceSignatures);
@@ -586,7 +585,7 @@
extraInterfaceSignatures.put(
iface,
new GenericSignature.ClassTypeSignature(
- rewriter.getEmulatedInterface(iface), signature));
+ helper.getEmulatedInterface(iface), signature));
}
collectEmulatedInterfacesWithPropagatedTypeArguments(
iface, signature, emulatesInterfaces, extraInterfaceSignatures);
@@ -598,7 +597,7 @@
if (emulatesInterfaces.contains(iface)) {
extraInterfaceSignatures.put(
iface,
- new GenericSignature.ClassTypeSignature(rewriter.getEmulatedInterface(iface)));
+ new GenericSignature.ClassTypeSignature(helper.getEmulatedInterface(iface)));
}
collectEmulatedInterfacesWithPropagatedTypeArguments(
iface, null, emulatesInterfaces, extraInterfaceSignatures);
@@ -611,7 +610,8 @@
DexClass clazz, EmulatedInterfaceInfo emulatedInterfaceInfo) {
AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
for (Wrapper<DexMethod> signature : emulatedInterfaceInfo.signatures.signatures) {
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(signature.get(), clazz);
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOnClass(signature.get(), clazz);
if (resolutionResult.isFailedResolution()) {
return true;
}
@@ -650,7 +650,7 @@
private void resolveForwardForSignature(
DexClass clazz, DexMethod method, Consumer<DexClassAndMethod> addForward) {
AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
- ResolutionResult resolutionResult = appInfo.resolveMethodOn(clazz, method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOn(clazz, method);
if (resolutionResult.isFailedResolution()
|| resolutionResult.asSuccessfulMemberResolutionResult().getResolvedMember().isStatic()) {
// When doing resolution we may find a static or private targets and bubble up the failed
@@ -717,7 +717,7 @@
// Here we use step-3 of resolution to find a maximally specific default interface method.
DexClassAndMethod result =
appInfo.lookupMaximallySpecificMethod(libraryMethod.getHolder(), method);
- if (result != null && rewriter.isEmulatedInterface(result.getHolderType())) {
+ if (result != null && helper.isEmulatedInterface(result.getHolderType())) {
addForward.accept(result);
}
}
@@ -731,9 +731,7 @@
}
private boolean dontRewrite(DexClassAndMethod method) {
- return needsLibraryInfo()
- && method.getHolder().isLibraryClass()
- && rewriter.dontRewrite(method);
+ return needsLibraryInfo() && method.getHolder().isLibraryClass() && helper.dontRewrite(method);
}
// Construction of actual forwarding methods.
@@ -818,7 +816,7 @@
// In desugared library, emulated interface methods can be overridden by retarget lib members.
DexMethod forwardMethod =
target.getHolder().isInterface()
- ? rewriter.ensureDefaultAsMethodOfCompanionClassStub(target).getReference()
+ ? helper.ensureDefaultAsMethodOfCompanionClassStub(target).getReference()
: appView.options().desugaredLibraryConfiguration.retargetMethod(target, appView);
DexEncodedMethod desugaringForwardingMethod =
DexEncodedMethod.createDesugaringForwardingMethod(
@@ -835,7 +833,7 @@
}
DexClass clazz = context.definitionFor(type, appView);
if (clazz == null) {
- context.reportMissingType(type, rewriter);
+ context.reportMissingType(type, helper);
return null;
}
return clazz;
@@ -935,7 +933,7 @@
for (DexType superiface : iface.interfaces.values) {
interfaceInfo = interfaceInfo.merge(visitInterfaceInfo(superiface, thisContext));
}
- return rewriter.isEmulatedInterface(iface.type)
+ return helper.isEmulatedInterface(iface.type)
? computeEmulatedInterfaceInfo(iface, interfaceInfo)
: computeInterfaceInfo(iface, interfaceInfo);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
index 1c29a09..c3fded8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
@@ -3,8 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.desugar.itf;
-import static com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.emulateInterfaceLibraryMethod;
-
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -33,15 +31,12 @@
public final class EmulatedInterfaceProcessor implements InterfaceDesugaringProcessor {
private final AppView<?> appView;
- private final InterfaceMethodRewriter rewriter;
- private final Map<DexType, DexType> emulatedInterfaces;
+ private final InterfaceDesugaringSyntheticHelper helper;
private final Map<DexType, List<DexType>> emulatedInterfacesHierarchy;
- EmulatedInterfaceProcessor(AppView<?> appView, InterfaceMethodRewriter rewriter) {
+ EmulatedInterfaceProcessor(AppView<?> appView) {
this.appView = appView;
- this.rewriter = rewriter;
- emulatedInterfaces =
- appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface();
+ helper = new InterfaceDesugaringSyntheticHelper(appView);
// Avoid the computation outside L8 since it is not needed.
emulatedInterfacesHierarchy =
appView.options().isDesugaredLibraryCompilation()
@@ -52,7 +47,7 @@
private Map<DexType, List<DexType>> processEmulatedInterfaceHierarchy() {
Map<DexType, List<DexType>> emulatedInterfacesHierarchy = new IdentityHashMap<>();
Set<DexType> processed = Sets.newIdentityHashSet();
- ArrayList<DexType> emulatedInterfacesSorted = new ArrayList<>(emulatedInterfaces.keySet());
+ ArrayList<DexType> emulatedInterfacesSorted = new ArrayList<>(helper.getEmulatedInterfaces());
emulatedInterfacesSorted.sort(DexType::compareTo);
for (DexType interfaceType : emulatedInterfacesSorted) {
processEmulatedInterfaceHierarchy(interfaceType, processed, emulatedInterfacesHierarchy);
@@ -76,7 +71,7 @@
LinkedList<DexType> workList = new LinkedList<>(Arrays.asList(theInterface.interfaces.values));
while (!workList.isEmpty()) {
DexType next = workList.removeLast();
- if (emulatedInterfaces.containsKey(next)) {
+ if (helper.isEmulatedInterface(next)) {
processEmulatedInterfaceHierarchy(next, processed, emulatedInterfacesHierarchy);
emulatedInterfacesHierarchy.get(next).add(interfaceType);
DexClass nextClass = appView.definitionFor(next);
@@ -89,7 +84,7 @@
DexProgramClass ensureEmulateInterfaceLibrary(
DexProgramClass emulatedInterface, InterfaceProcessingDesugaringEventConsumer eventConsumer) {
- assert rewriter.isEmulatedInterface(emulatedInterface.type);
+ assert helper.isEmulatedInterface(emulatedInterface.type);
DexProgramClass emulateInterfaceClass =
appView
.getSyntheticItems()
@@ -108,7 +103,7 @@
ignored -> {});
emulateInterfaceClass.forEachProgramMethod(eventConsumer::acceptEmulatedInterfaceMethod);
assert emulateInterfaceClass.getType()
- == InterfaceMethodRewriter.getEmulateLibraryInterfaceClassType(
+ == InterfaceDesugaringSyntheticHelper.getEmulateLibraryInterfaceClassType(
emulatedInterface.type, appView.dexItemFactory());
return emulateInterfaceClass;
}
@@ -119,20 +114,23 @@
DexMethod libraryMethod =
method
.getReference()
- .withHolder(emulatedInterfaces.get(theInterface.type), appView.dexItemFactory());
+ .withHolder(helper.getEmulatedInterface(theInterface.type), appView.dexItemFactory());
DexMethod companionMethod =
- rewriter.ensureDefaultAsMethodOfProgramCompanionClassStub(method).getReference();
+ helper.ensureDefaultAsMethodOfProgramCompanionClassStub(method).getReference();
List<Pair<DexType, DexMethod>> extraDispatchCases =
getDispatchCases(method, theInterface, companionMethod);
- DexMethod emulatedMethod = emulateInterfaceLibraryMethod(method, appView.dexItemFactory());
+ DexMethod emulatedMethod =
+ InterfaceDesugaringSyntheticHelper.emulateInterfaceLibraryMethod(
+ method, appView.dexItemFactory());
methodBuilder
.setName(emulatedMethod.getName())
.setProto(emulatedMethod.getProto())
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
.setCode(
- theMethod ->
+ emulatedInterfaceMethod ->
new EmulateInterfaceSyntheticCfCodeProvider(
- theMethod.getHolderType(),
+ emulatedInterfaceMethod.getHolderType(),
+ method.getHolderType(),
companionMethod,
libraryMethod,
extraDispatchCases,
@@ -185,7 +183,7 @@
extraDispatchCases.add(
new Pair<>(
subInterfaceClass.type,
- InterfaceMethodRewriter.defaultAsMethodOfCompanionClass(
+ InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass(
result.getReference(), appView.dexItemFactory())));
}
}
@@ -214,7 +212,7 @@
public void process(
DexProgramClass emulatedInterface, InterfaceProcessingDesugaringEventConsumer eventConsumer) {
if (!appView.options().isDesugaredLibraryCompilation()
- || !rewriter.isEmulatedInterface(emulatedInterface.type)
+ || !helper.isEmulatedInterface(emulatedInterface.type)
|| appView.isAlreadyLibraryDesugared(emulatedInterface)) {
return;
}
@@ -233,7 +231,7 @@
}
private void warnMissingEmulatedInterfaces() {
- for (DexType interfaceType : emulatedInterfaces.keySet()) {
+ for (DexType interfaceType : helper.getEmulatedInterfaces()) {
DexClass theInterface = appView.definitionFor(interfaceType);
if (theInterface == null) {
warnMissingEmulatedInterface(interfaceType);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringForTesting.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringForTesting.java
index 81ec904..a3e0164 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringForTesting.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringForTesting.java
@@ -7,22 +7,22 @@
public class InterfaceDesugaringForTesting {
public static String getEmulateLibraryClassNameSuffix() {
- return InterfaceMethodRewriter.EMULATE_LIBRARY_CLASS_NAME_SUFFIX;
+ return InterfaceDesugaringSyntheticHelper.EMULATE_LIBRARY_CLASS_NAME_SUFFIX;
}
public static String getCompanionClassNameSuffix() {
- return InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
+ return InterfaceDesugaringSyntheticHelper.COMPANION_CLASS_NAME_SUFFIX;
}
public static String getDefaultMethodPrefix() {
- return InterfaceMethodRewriter.DEFAULT_METHOD_PREFIX;
+ return InterfaceDesugaringSyntheticHelper.DEFAULT_METHOD_PREFIX;
}
public static String getPrivateMethodPrefix() {
- return InterfaceMethodRewriter.PRIVATE_METHOD_PREFIX;
+ return InterfaceDesugaringSyntheticHelper.PRIVATE_METHOD_PREFIX;
}
public static String getCompanionClassDescriptor(String descriptor) {
- return InterfaceMethodRewriter.getCompanionClassDescriptor(descriptor);
+ return InterfaceDesugaringSyntheticHelper.getCompanionClassDescriptor(descriptor);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
new file mode 100644
index 0000000..ad5aa5e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
@@ -0,0 +1,358 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.itf;
+
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClasspathOrLibraryClass;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+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.GenericSignature.MethodTypeSignature;
+import com.android.tools.r8.graph.InvalidCode;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Pair;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Predicate;
+
+public class InterfaceDesugaringSyntheticHelper {
+
+ // Use InterfaceDesugaringForTesting for public accesses in tests.
+ static final String EMULATE_LIBRARY_CLASS_NAME_SUFFIX = "$-EL";
+ static final String COMPANION_CLASS_NAME_SUFFIX = "$-CC";
+ static final String DEFAULT_METHOD_PREFIX = "$default$";
+ static final String PRIVATE_METHOD_PREFIX = "$private$";
+
+ private final AppView<?> appView;
+ private final Map<DexType, DexType> emulatedInterfaces;
+ private final Predicate<DexType> shouldIgnoreFromReportsPredicate;
+
+ public InterfaceDesugaringSyntheticHelper(AppView<?> appView) {
+ this.appView = appView;
+ emulatedInterfaces =
+ appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface();
+
+ this.shouldIgnoreFromReportsPredicate = getShouldIgnoreFromReportsPredicate(appView);
+ }
+
+ boolean isEmulatedInterface(DexType itf) {
+ return emulatedInterfaces.containsKey(itf);
+ }
+
+ boolean isRewrittenEmulatedInterface(DexType itf) {
+ return emulatedInterfaces.containsValue(itf);
+ }
+
+ Set<DexType> getEmulatedInterfaces() {
+ return emulatedInterfaces.keySet();
+ }
+
+ DexType getEmulatedInterface(DexType type) {
+ return emulatedInterfaces.get(type);
+ }
+
+ boolean isInDesugaredLibrary(DexClass clazz) {
+ assert clazz.isLibraryClass() || appView.options().isDesugaredLibraryCompilation();
+ if (isEmulatedInterface(clazz.type)) {
+ return true;
+ }
+ return appView.rewritePrefix.hasRewrittenType(clazz.type, appView);
+ }
+
+ boolean dontRewrite(DexClassAndMethod method) {
+ for (Pair<DexType, DexString> dontRewrite :
+ appView.options().desugaredLibraryConfiguration.getDontRewriteInvocation()) {
+ if (method.getHolderType() == dontRewrite.getFirst()
+ && method.getName() == dontRewrite.getSecond()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ final boolean isCompatibleDefaultMethod(DexEncodedMethod method) {
+ assert !method.accessFlags.isConstructor();
+ assert !method.accessFlags.isStatic();
+
+ if (method.accessFlags.isAbstract()) {
+ return false;
+ }
+ if (method.accessFlags.isNative()) {
+ throw new Unimplemented("Native default interface methods are not yet supported.");
+ }
+ if (!method.accessFlags.isPublic()) {
+ // NOTE: even though the class is allowed to have non-public interface methods
+ // with code, for example private methods, all such methods we are aware of are
+ // created by the compiler for stateful lambdas and they must be converted into
+ // static methods by lambda desugaring by this time.
+ throw new Unimplemented("Non public default interface methods are not yet supported.");
+ }
+ return true;
+ }
+
+ public static DexMethod emulateInterfaceLibraryMethod(
+ DexClassAndMethod method, DexItemFactory factory) {
+ return factory.createMethod(
+ getEmulateLibraryInterfaceClassType(method.getHolderType(), factory),
+ factory.prependTypeToProto(method.getHolderType(), method.getProto()),
+ method.getName());
+ }
+
+ private static String getEmulateLibraryInterfaceClassDescriptor(String descriptor) {
+ return descriptor.substring(0, descriptor.length() - 1)
+ + EMULATE_LIBRARY_CLASS_NAME_SUFFIX
+ + ";";
+ }
+
+ public static DexType getEmulateLibraryInterfaceClassType(DexType type, DexItemFactory factory) {
+ assert type.isClassType();
+ String descriptor = type.descriptor.toString();
+ String elTypeDescriptor = getEmulateLibraryInterfaceClassDescriptor(descriptor);
+ return factory.createSynthesizedType(elTypeDescriptor);
+ }
+
+ public static String getCompanionClassDescriptor(String descriptor) {
+ return descriptor.substring(0, descriptor.length() - 1) + COMPANION_CLASS_NAME_SUFFIX + ";";
+ }
+
+ // Gets the companion class for the interface `type`.
+ static DexType getCompanionClassType(DexType type, DexItemFactory factory) {
+ assert type.isClassType();
+ String descriptor = type.descriptor.toString();
+ String ccTypeDescriptor = getCompanionClassDescriptor(descriptor);
+ return factory.createSynthesizedType(ccTypeDescriptor);
+ }
+
+ // Checks if `type` is a companion class.
+ public static boolean isCompanionClassType(DexType type) {
+ return type.descriptor.toString().endsWith(COMPANION_CLASS_NAME_SUFFIX + ";");
+ }
+
+ public static boolean isEmulatedLibraryClassType(DexType type) {
+ return type.descriptor.toString().endsWith(EMULATE_LIBRARY_CLASS_NAME_SUFFIX + ";");
+ }
+
+ // Gets the interface class for a companion class `type`.
+ DexType getInterfaceClassType(DexType type) {
+ return getInterfaceClassType(type, appView.dexItemFactory());
+ }
+
+ // Gets the interface class for a companion class `type`.
+ public static DexType getInterfaceClassType(DexType type, DexItemFactory factory) {
+ assert isCompanionClassType(type);
+ String descriptor = type.descriptor.toString();
+ String interfaceTypeDescriptor =
+ descriptor.substring(0, descriptor.length() - 1 - COMPANION_CLASS_NAME_SUFFIX.length())
+ + ";";
+ return factory.createType(interfaceTypeDescriptor);
+ }
+
+ DexClassAndMethod ensureDefaultAsMethodOfCompanionClassStub(DexClassAndMethod method) {
+ if (method.isProgramMethod()) {
+ return ensureDefaultAsMethodOfProgramCompanionClassStub(method.asProgramMethod());
+ }
+ ClasspathOrLibraryClass context = method.getHolder().asClasspathOrLibraryClass();
+ DexMethod companionMethodReference =
+ defaultAsMethodOfCompanionClass(method.getReference(), appView.dexItemFactory());
+ return ensureMethodOfClasspathCompanionClassStub(companionMethodReference, context, appView);
+ }
+
+ DexClassAndMethod ensureStaticAsMethodOfCompanionClassStub(DexClassAndMethod method) {
+ if (method.isProgramMethod()) {
+ return ensureStaticAsMethodOfProgramCompanionClassStub(method.asProgramMethod());
+ } else {
+ ClasspathOrLibraryClass context = method.getHolder().asClasspathOrLibraryClass();
+ DexMethod companionMethodReference = staticAsMethodOfCompanionClass(method);
+ return ensureMethodOfClasspathCompanionClassStub(companionMethodReference, context, appView);
+ }
+ }
+
+ ProgramMethod ensureDefaultAsMethodOfProgramCompanionClassStub(ProgramMethod method) {
+ DexEncodedMethod virtual = method.getDefinition();
+ DexMethod companionMethod =
+ defaultAsMethodOfCompanionClass(method.getReference(), appView.dexItemFactory());
+ return InterfaceProcessor.ensureCompanionMethod(
+ method.getHolder(),
+ companionMethod.getName(),
+ companionMethod.getProto(),
+ appView,
+ methodBuilder -> {
+ MethodAccessFlags newFlags = method.getAccessFlags().copy();
+ newFlags.promoteToStatic();
+ methodBuilder
+ .setAccessFlags(newFlags)
+ .setGenericSignature(MethodTypeSignature.noSignature())
+ .setAnnotations(
+ virtual
+ .annotations()
+ .methodParametersWithFakeThisArguments(appView.dexItemFactory()))
+ .setParameterAnnotationsList(
+ virtual.getParameterAnnotations().withFakeThisParameter())
+ // TODO(b/183998768): Once R8 desugars in the enqueuer this should set an invalid
+ // code to ensure it is never used before desugared and installed.
+ .setCode(
+ syntheticMethod ->
+ appView.enableWholeProgramOptimizations()
+ ? virtual
+ .getCode()
+ .getCodeAsInlining(syntheticMethod, method.getReference())
+ : InvalidCode.getInstance());
+ });
+ }
+
+ ProgramMethod ensurePrivateAsMethodOfProgramCompanionClassStub(ProgramMethod method) {
+ DexMethod companionMethod =
+ privateAsMethodOfCompanionClass(method.getReference(), appView.dexItemFactory());
+ DexEncodedMethod definition = method.getDefinition();
+ return InterfaceProcessor.ensureCompanionMethod(
+ method.getHolder(),
+ companionMethod.getName(),
+ companionMethod.getProto(),
+ appView,
+ methodBuilder -> {
+ MethodAccessFlags newFlags = definition.getAccessFlags().copy();
+ assert newFlags.isPrivate();
+ newFlags.promoteToPublic();
+ newFlags.promoteToStatic();
+ methodBuilder
+ .setAccessFlags(newFlags)
+ .setGenericSignature(definition.getGenericSignature())
+ .setAnnotations(definition.annotations())
+ // TODO(b/183998768): Should this not also be updating with a fake 'this'
+ .setParameterAnnotationsList(definition.getParameterAnnotations())
+ // TODO(b/183998768): Once R8 desugars in the enqueuer this should set an invalid
+ // code to ensure it is never used before desugared and installed.
+ .setCode(
+ syntheticMethod ->
+ appView.enableWholeProgramOptimizations()
+ ? definition
+ .getCode()
+ .getCodeAsInlining(syntheticMethod, method.getReference())
+ : InvalidCode.getInstance());
+ });
+ }
+
+ // Represent a static interface method as a method of companion class.
+ final DexMethod staticAsMethodOfCompanionClass(DexClassAndMethod method) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ DexType companionClassType = getCompanionClassType(method.getHolderType(), dexItemFactory);
+ DexMethod rewritten = method.getReference().withHolder(companionClassType, dexItemFactory);
+ return rewritten;
+ }
+
+ private static DexMethod instanceAsMethodOfCompanionClass(
+ DexMethod method, String prefix, DexItemFactory factory) {
+ // Add an implicit argument to represent the receiver.
+ DexType[] params = method.proto.parameters.values;
+ DexType[] newParams = new DexType[params.length + 1];
+ newParams[0] = method.holder;
+ System.arraycopy(params, 0, newParams, 1, params.length);
+
+ // Add prefix to avoid name conflicts.
+ return factory.createMethod(
+ getCompanionClassType(method.holder, factory),
+ factory.createProto(method.proto.returnType, newParams),
+ factory.createString(prefix + method.name.toString()));
+ }
+
+ // Represent a default interface method as a method of companion class.
+ public static DexMethod defaultAsMethodOfCompanionClass(
+ DexMethod method, DexItemFactory factory) {
+ return instanceAsMethodOfCompanionClass(method, DEFAULT_METHOD_PREFIX, factory);
+ }
+
+ // Represent a private instance interface method as a method of companion class.
+ static DexMethod privateAsMethodOfCompanionClass(DexMethod method, DexItemFactory factory) {
+ // Add an implicit argument to represent the receiver.
+ return instanceAsMethodOfCompanionClass(method, PRIVATE_METHOD_PREFIX, factory);
+ }
+
+ DexMethod privateAsMethodOfCompanionClass(DexClassAndMethod method) {
+ return privateAsMethodOfCompanionClass(method.getReference(), appView.dexItemFactory());
+ }
+
+ private static DexClassAndMethod ensureMethodOfClasspathCompanionClassStub(
+ DexMethod companionMethodReference, ClasspathOrLibraryClass context, AppView<?> appView) {
+ return appView
+ .getSyntheticItems()
+ .ensureFixedClasspathClassMethod(
+ companionMethodReference.getName(),
+ companionMethodReference.getProto(),
+ SyntheticKind.COMPANION_CLASS,
+ context,
+ appView,
+ classBuilder -> {},
+ methodBuilder ->
+ methodBuilder
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setCode(DexEncodedMethod::buildEmptyThrowingCfCode));
+ }
+
+ ProgramMethod ensureStaticAsMethodOfProgramCompanionClassStub(ProgramMethod method) {
+ DexMethod companionMethodReference = staticAsMethodOfCompanionClass(method);
+ DexEncodedMethod definition = method.getDefinition();
+ return InterfaceProcessor.ensureCompanionMethod(
+ method.getHolder(),
+ companionMethodReference.getName(),
+ companionMethodReference.getProto(),
+ appView,
+ methodBuilder -> {
+ MethodAccessFlags newFlags = definition.getAccessFlags().copy();
+ newFlags.promoteToPublic();
+ methodBuilder
+ .setAccessFlags(newFlags)
+ .setGenericSignature(definition.getGenericSignature())
+ .setAnnotations(definition.annotations())
+ .setParameterAnnotationsList(definition.getParameterAnnotations())
+ // TODO(b/183998768): Once R8 desugars in the enqueuer this should set an invalid
+ // code to ensure it is never used before desugared and installed.
+ .setCode(
+ syntheticMethod ->
+ appView.enableWholeProgramOptimizations()
+ ? definition
+ .getCode()
+ .getCodeAsInlining(syntheticMethod, method.getReference())
+ : InvalidCode.getInstance());
+ });
+ }
+
+ private Predicate<DexType> getShouldIgnoreFromReportsPredicate(AppView<?> appView) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ InternalOptions options = appView.options();
+ DexString companionClassNameDescriptorSuffix =
+ dexItemFactory.createString(
+ InterfaceDesugaringSyntheticHelper.COMPANION_CLASS_NAME_SUFFIX + ";");
+
+ return type -> {
+ DexString descriptor = type.getDescriptor();
+ return appView.rewritePrefix.hasRewrittenType(type, appView)
+ || descriptor.endsWith(companionClassNameDescriptorSuffix)
+ || isRewrittenEmulatedInterface(type)
+ || options.desugaredLibraryConfiguration.getCustomConversions().containsValue(type)
+ || appView.getDontWarnConfiguration().matches(type);
+ };
+ }
+
+ boolean shouldIgnoreFromReports(DexType missing) {
+ return shouldIgnoreFromReportsPredicate.test(missing);
+ }
+
+ void warnMissingInterface(DexClass classToDesugar, DexClass implementing, DexType missing) {
+ // We use contains() on non hashed collection, but we know it's a 8 cases collection.
+ // j$ interfaces won't be missing, they are in the desugared library.
+ if (shouldIgnoreFromReports(missing)) {
+ return;
+ }
+ appView.options().warningMissingInterfaceForDesugar(classToDesugar, implementing, missing);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
index 71844b0..fafc920 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
+import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -25,30 +26,28 @@
private final Flavor flavour;
private final List<InterfaceDesugaringProcessor> interfaceDesugaringProcessors;
- InterfaceMethodProcessorFacade(
- AppView<?> appView, Flavor flavour, InterfaceMethodRewriter rewriter) {
+ InterfaceMethodProcessorFacade(AppView<?> appView, Flavor flavour) {
this.appView = appView;
this.flavour = flavour;
- interfaceDesugaringProcessors = instantiateInterfaceDesugaringProcessors(appView, rewriter);
+ interfaceDesugaringProcessors = instantiateInterfaceDesugaringProcessors(appView);
}
private List<InterfaceDesugaringProcessor> instantiateInterfaceDesugaringProcessors(
- AppView<?> appView, InterfaceMethodRewriter rewriter) {
+ AppView<?> appView) {
// During L8 compilation, emulated interfaces are processed to be renamed, to have
// their interfaces fixed-up and to generate the emulated dispatch code.
- EmulatedInterfaceProcessor emulatedInterfaceProcessor =
- new EmulatedInterfaceProcessor(appView, rewriter);
+ EmulatedInterfaceProcessor emulatedInterfaceProcessor = new EmulatedInterfaceProcessor(appView);
// Process all classes first. Add missing forwarding methods to
// replace desugared default interface methods.
- ClassProcessor classProcessor = new ClassProcessor(appView, rewriter);
+ ClassProcessor classProcessor = new ClassProcessor(appView);
// Process interfaces, create companion or dispatch class if needed, move static
// methods to companion class, copy default interface methods to companion classes,
// make original default methods abstract, remove bridge methods, create dispatch
// classes if needed.
- InterfaceProcessor interfaceProcessor = new InterfaceProcessor(appView, rewriter);
+ InterfaceProcessor interfaceProcessor = new InterfaceProcessor(appView);
// The processors can be listed in any order.
return ImmutableList.of(classProcessor, interfaceProcessor, emulatedInterfaceProcessor);
@@ -60,7 +59,7 @@
CollectingInterfaceDesugaringEventConsumer eventConsumer =
new CollectingInterfaceDesugaringEventConsumer();
- processClassesConcurrently(eventConsumer, executorService);
+ processClassesConcurrently(appView.appInfo().classes(), eventConsumer, executorService);
converter.processMethodsConcurrently(
eventConsumer.getSortedSynthesizedMethods(), executorService);
}
@@ -101,11 +100,12 @@
}
private void processClassesConcurrently(
- InterfaceProcessingDesugaringEventConsumer eventConsumer, ExecutorService executorService)
+ Collection<DexProgramClass> programClasses,
+ InterfaceProcessingDesugaringEventConsumer eventConsumer,
+ ExecutorService executorService)
throws ExecutionException {
ThreadUtils.processItems(
- Iterables.filter(
- appView.appInfo().classes(), (DexProgramClass clazz) -> shouldProcess(clazz, flavour)),
+ Iterables.filter(programClasses, (DexProgramClass clazz) -> shouldProcess(clazz, flavour)),
clazz -> {
for (InterfaceDesugaringProcessor processor : interfaceDesugaringProcessors) {
processor.process(clazz, eventConsumer);
@@ -119,10 +119,12 @@
@Override
public void postProcessingDesugaring(
- CfPostProcessingDesugaringEventConsumer eventConsumer, ExecutorService executorService)
+ Collection<DexProgramClass> programClasses,
+ CfPostProcessingDesugaringEventConsumer eventConsumer,
+ ExecutorService executorService)
throws ExecutionException {
// TODO(b/183998768): Would be nice to use the ClassProcessing for the processing of classes,
// and do here only the finalization.
- processClassesConcurrently(eventConsumer, executorService);
+ processClassesConcurrently(programClasses, eventConsumer, executorService);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 3e93691..38ad3e4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -24,7 +24,6 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.ClasspathOrLibraryClass;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
@@ -36,11 +35,9 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DexValue;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
-import com.android.tools.r8.graph.InvalidCode;
import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
@@ -69,11 +66,9 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.synthesis.SyntheticNaming;
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IteratorUtils;
-import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import com.android.tools.r8.utils.structural.Ordered;
@@ -90,7 +85,6 @@
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
-import java.util.function.Predicate;
//
// Default and static interface method desugaring rewriter (note that lambda
@@ -120,16 +114,10 @@
//
public final class InterfaceMethodRewriter implements CfInstructionDesugaring {
- // Use InterfaceDesugaringForTesting for public accesses in tests.
- static final String EMULATE_LIBRARY_CLASS_NAME_SUFFIX = "$-EL";
- static final String COMPANION_CLASS_NAME_SUFFIX = "$-CC";
- static final String DEFAULT_METHOD_PREFIX = "$default$";
- static final String PRIVATE_METHOD_PREFIX = "$private$";
-
private final AppView<?> appView;
private final InternalOptions options;
final DexItemFactory factory;
- private final Map<DexType, DexType> emulatedInterfaces;
+ private final InterfaceDesugaringSyntheticHelper helper;
// The emulatedMethod set is there to avoid doing the emulated look-up too often.
private final Set<DexString> emulatedMethods = Sets.newIdentityHashSet();
@@ -139,8 +127,6 @@
// Caches default interface method info for already processed interfaces.
private final Map<DexType, DefaultMethodsHelper.Collection> cache = new ConcurrentHashMap<>();
- private final Predicate<DexType> shouldIgnoreFromReportsPredicate;
-
// This is used to filter out double desugaring on backported methods.
private final BackportedMethodRewriter backportedMethodRewriter;
private final DesugaredLibraryRetargeter desugaredLibraryRetargeter;
@@ -163,8 +149,7 @@
this.desugaredLibraryRetargeter = desugaredLibraryRetargeter;
this.options = appView.options();
this.factory = appView.dexItemFactory();
- this.emulatedInterfaces = options.desugaredLibraryConfiguration.getEmulateLibraryInterface();
- this.shouldIgnoreFromReportsPredicate = getShouldIgnoreFromReportsPredicate(appView);
+ this.helper = new InterfaceDesugaringSyntheticHelper(appView);
initializeEmulatedInterfaceVariables();
}
@@ -176,8 +161,7 @@
this.desugaredLibraryRetargeter = null;
this.options = appView.options();
this.factory = appView.dexItemFactory();
- this.emulatedInterfaces = options.desugaredLibraryConfiguration.getEmulateLibraryInterface();
- this.shouldIgnoreFromReportsPredicate = getShouldIgnoreFromReportsPredicate(appView);
+ this.helper = new InterfaceDesugaringSyntheticHelper(appView);
initializeEmulatedInterfaceVariables();
}
@@ -214,6 +198,10 @@
}
}
+ public Set<DexString> getEmulatedMethods() {
+ return emulatedMethods;
+ }
+
private void initializeEmulatedInterfaceVariables() {
Map<DexType, DexType> emulateLibraryInterface =
options.desugaredLibraryConfiguration.getEmulateLibraryInterface();
@@ -234,28 +222,29 @@
DexType emulatedInterface, String rewrittenEmulatedInterface) {
addCompanionClassRewriteRule(emulatedInterface, rewrittenEmulatedInterface);
appView.rewritePrefix.rewriteType(
- getEmulateLibraryInterfaceClassType(emulatedInterface, factory),
+ InterfaceDesugaringSyntheticHelper.getEmulateLibraryInterfaceClassType(
+ emulatedInterface, factory),
factory.createType(
DescriptorUtils.javaTypeToDescriptor(
- rewrittenEmulatedInterface + EMULATE_LIBRARY_CLASS_NAME_SUFFIX)));
+ rewrittenEmulatedInterface
+ + InterfaceDesugaringSyntheticHelper.EMULATE_LIBRARY_CLASS_NAME_SUFFIX)));
}
- void addCompanionClassRewriteRule(DexType interfaceType, String rewrittenType) {
+ private void addCompanionClassRewriteRule(DexType interfaceType, String rewrittenType) {
addCompanionClassRewriteRule(interfaceType, rewrittenType, appView);
}
static void addCompanionClassRewriteRule(
DexType interfaceType, String rewrittenType, AppView<?> appView) {
appView.rewritePrefix.rewriteType(
- getCompanionClassType(interfaceType, appView.dexItemFactory()),
+ InterfaceDesugaringSyntheticHelper.getCompanionClassType(
+ interfaceType, appView.dexItemFactory()),
appView
.dexItemFactory()
.createType(
- DescriptorUtils.javaTypeToDescriptor(rewrittenType + COMPANION_CLASS_NAME_SUFFIX)));
- }
-
- boolean isEmulatedInterface(DexType itf) {
- return emulatedInterfaces.containsKey(itf);
+ DescriptorUtils.javaTypeToDescriptor(
+ rewrittenType
+ + InterfaceDesugaringSyntheticHelper.COMPANION_CLASS_NAME_SUFFIX)));
}
public boolean needsRewriting(DexMethod method, Type invokeType, ProgramMethod context) {
@@ -562,10 +551,6 @@
return replacement;
}
- DexType getEmulatedInterface(DexType itf) {
- return emulatedInterfaces.get(itf);
- }
-
private void leavingStaticInvokeToInterface(ProgramMethod method) {
// When leaving static interface method invokes possibly upgrade the class file
// version, but don't go above the initial class file version. If the input was
@@ -740,12 +725,15 @@
if (directTarget.getDefinition().isPrivateMethod()) {
companionMethod =
directTarget.isProgramMethod()
- ? ensurePrivateAsMethodOfProgramCompanionClassStub(directTarget.asProgramMethod())
+ ? helper
+ .ensurePrivateAsMethodOfProgramCompanionClassStub(
+ directTarget.asProgramMethod())
.getReference()
// TODO(b/183998768): Why does this not create a stub on the class path?
- : privateAsMethodOfCompanionClass(directTarget);
+ : helper.privateAsMethodOfCompanionClass(directTarget);
} else {
- companionMethod = ensureDefaultAsMethodOfCompanionClassStub(directTarget).getReference();
+ companionMethod =
+ helper.ensureDefaultAsMethodOfCompanionClassStub(directTarget).getReference();
}
return rewriteInvoke.apply(companionMethod);
} else {
@@ -756,7 +744,7 @@
// This is a invoke-direct call to a virtual method.
assert invokeNeedsRewriting(invokedMethod, DIRECT);
return rewriteInvoke.apply(
- ensureDefaultAsMethodOfCompanionClassStub(virtualTarget).getReference());
+ helper.ensureDefaultAsMethodOfCompanionClassStub(virtualTarget).getReference());
} else {
// The below assert is here because a well-type program should have a target, but we
// cannot throw a compilation error, since we have no knowledge about the input.
@@ -868,7 +856,7 @@
assert resolutionResult.getResolvedMethod().isStatic();
assert invokeNeedsRewriting(invokedMethod, STATIC);
DexClassAndMethod companionMethod =
- ensureStaticAsMethodOfCompanionClassStub(resolutionResult.getResolutionPair());
+ helper.ensureStaticAsMethodOfCompanionClassStub(resolutionResult.getResolutionPair());
return rewriteInvoke.apply(companionMethod.getReference());
}
@@ -910,11 +898,12 @@
return rewriteToThrow.apply(null);
}
return rewriteInvoke.apply(
- privateAsMethodOfCompanionClass(resolutionResult.getResolutionPair()));
+ helper.privateAsMethodOfCompanionClass(resolutionResult.getResolutionPair()));
} else {
DexMethod amendedMethod = amendDefaultMethod(context.getHolder(), invokedMethod);
return rewriteInvoke.apply(
- defaultAsMethodOfCompanionClass(amendedMethod, appView.dexItemFactory()));
+ InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass(
+ amendedMethod, appView.dexItemFactory()));
}
}
@@ -928,7 +917,7 @@
if (holder.isLibraryClass() && holder.isInterface()) {
assert invokeNeedsRewriting(invokedMethod, SUPER);
return rewriteInvoke.apply(
- ensureDefaultAsMethodOfCompanionClassStub(target).getReference());
+ helper.ensureDefaultAsMethodOfCompanionClassStub(target).getReference());
}
}
}
@@ -956,7 +945,8 @@
assert false;
return null;
}
- DexClassAndMethod companionMethod = ensureDefaultAsMethodOfCompanionClassStub(emulatedMethod);
+ DexClassAndMethod companionMethod =
+ helper.ensureDefaultAsMethodOfCompanionClassStub(emulatedMethod);
return rewriteInvoke.apply(companionMethod.getReference());
}
return null;
@@ -980,7 +970,7 @@
|| appView.options().isDesugaredLibraryCompilation())) {
DexClassAndMethod defaultMethod =
appView.definitionFor(emulatedItf).lookupClassMethod(invokedMethod);
- if (defaultMethod != null && !dontRewrite(defaultMethod)) {
+ if (defaultMethod != null && !helper.dontRewrite(defaultMethod)) {
assert !defaultMethod.getAccessFlags().isAbstract();
return defaultMethod;
}
@@ -995,7 +985,8 @@
DexClassAndMethod defaultMethod =
defaultMethodForEmulatedDispatchOrNull(invokedMethod, interfaceBit);
if (defaultMethod != null) {
- return rewriteInvoke.apply(emulateInterfaceLibraryMethod(defaultMethod, factory));
+ return rewriteInvoke.apply(
+ InterfaceDesugaringSyntheticHelper.emulateInterfaceLibraryMethod(defaultMethod, factory));
}
return null;
}
@@ -1089,54 +1080,14 @@
// interfaces.
return null;
}
- if (!singleTarget.isAbstract() && isEmulatedInterface(singleTarget.getHolderType())) {
+ if (!singleTarget.isAbstract() && helper.isEmulatedInterface(singleTarget.getHolderType())) {
return singleTarget.getHolderType();
}
return null;
}
private boolean isNonDesugaredLibraryClass(DexClass clazz) {
- return clazz.isLibraryClass() && !isInDesugaredLibrary(clazz);
- }
-
- boolean isInDesugaredLibrary(DexClass clazz) {
- assert clazz.isLibraryClass() || options.isDesugaredLibraryCompilation();
- if (emulatedInterfaces.containsKey(clazz.type)) {
- return true;
- }
- return appView.rewritePrefix.hasRewrittenType(clazz.type, appView);
- }
-
- boolean dontRewrite(DexClassAndMethod method) {
- for (Pair<DexType, DexString> dontRewrite :
- options.desugaredLibraryConfiguration.getDontRewriteInvocation()) {
- if (method.getHolderType() == dontRewrite.getFirst()
- && method.getName() == dontRewrite.getSecond()) {
- return true;
- }
- }
- return false;
- }
-
- public static DexMethod emulateInterfaceLibraryMethod(
- DexClassAndMethod method, DexItemFactory factory) {
- return factory.createMethod(
- getEmulateLibraryInterfaceClassType(method.getHolderType(), factory),
- factory.prependTypeToProto(method.getHolderType(), method.getProto()),
- method.getName());
- }
-
- private static String getEmulateLibraryInterfaceClassDescriptor(String descriptor) {
- return descriptor.substring(0, descriptor.length() - 1)
- + EMULATE_LIBRARY_CLASS_NAME_SUFFIX
- + ";";
- }
-
- public static DexType getEmulateLibraryInterfaceClassType(DexType type, DexItemFactory factory) {
- assert type.isClassType();
- String descriptor = type.descriptor.toString();
- String elTypeDescriptor = getEmulateLibraryInterfaceClassDescriptor(descriptor);
- return factory.createSynthesizedType(elTypeDescriptor);
+ return clazz.isLibraryClass() && !helper.isInDesugaredLibrary(clazz);
}
private void reportStaticInterfaceMethodHandle(ProgramMethod context, DexMethodHandle handle) {
@@ -1155,147 +1106,6 @@
}
}
- // Use InterfaceDesugaringForTesting for public accesses in tests.
- static String getCompanionClassDescriptor(String descriptor) {
- return descriptor.substring(0, descriptor.length() - 1) + COMPANION_CLASS_NAME_SUFFIX + ";";
- }
-
- // Gets the companion class for the interface `type`.
- static DexType getCompanionClassType(DexType type, DexItemFactory factory) {
- assert type.isClassType();
- String descriptor = type.descriptor.toString();
- String ccTypeDescriptor = getCompanionClassDescriptor(descriptor);
- return factory.createSynthesizedType(ccTypeDescriptor);
- }
-
- // Checks if `type` is a companion class.
- public static boolean isCompanionClassType(DexType type) {
- return type.descriptor.toString().endsWith(COMPANION_CLASS_NAME_SUFFIX + ";");
- }
-
- public static boolean isEmulatedLibraryClassType(DexType type) {
- return type.descriptor.toString().endsWith(EMULATE_LIBRARY_CLASS_NAME_SUFFIX + ";");
- }
-
- // Gets the interface class for a companion class `type`.
- private DexType getInterfaceClassType(DexType type) {
- return getInterfaceClassType(type, factory);
- }
-
- // Gets the interface class for a companion class `type`.
- public static DexType getInterfaceClassType(DexType type, DexItemFactory factory) {
- assert isCompanionClassType(type);
- String descriptor = type.descriptor.toString();
- String interfaceTypeDescriptor =
- descriptor.substring(0, descriptor.length() - 1 - COMPANION_CLASS_NAME_SUFFIX.length())
- + ";";
- return factory.createType(interfaceTypeDescriptor);
- }
-
- DexClassAndMethod ensureDefaultAsMethodOfCompanionClassStub(DexClassAndMethod method) {
- if (method.isProgramMethod()) {
- return ensureDefaultAsMethodOfProgramCompanionClassStub(method.asProgramMethod());
- }
- ClasspathOrLibraryClass context = method.getHolder().asClasspathOrLibraryClass();
- DexMethod companionMethodReference =
- defaultAsMethodOfCompanionClass(method.getReference(), appView.dexItemFactory());
- return ensureMethodOfClasspathCompanionClassStub(companionMethodReference, context, appView);
- }
-
- DexClassAndMethod ensureStaticAsMethodOfCompanionClassStub(DexClassAndMethod method) {
- if (method.isProgramMethod()) {
- return ensureStaticAsMethodOfProgramCompanionClassStub(method.asProgramMethod());
- } else {
- ClasspathOrLibraryClass context = method.getHolder().asClasspathOrLibraryClass();
- DexMethod companionMethodReference = staticAsMethodOfCompanionClass(method);
- return ensureMethodOfClasspathCompanionClassStub(companionMethodReference, context, appView);
- }
- }
-
- ProgramMethod ensureDefaultAsMethodOfProgramCompanionClassStub(ProgramMethod method) {
- DexEncodedMethod virtual = method.getDefinition();
- DexMethod companionMethod =
- defaultAsMethodOfCompanionClass(method.getReference(), appView.dexItemFactory());
- return InterfaceProcessor.ensureCompanionMethod(
- method.getHolder(),
- companionMethod.getName(),
- companionMethod.getProto(),
- appView,
- methodBuilder -> {
- MethodAccessFlags newFlags = method.getAccessFlags().copy();
- newFlags.promoteToStatic();
- methodBuilder
- .setAccessFlags(newFlags)
- .setGenericSignature(MethodTypeSignature.noSignature())
- .setAnnotations(
- virtual
- .annotations()
- .methodParametersWithFakeThisArguments(appView.dexItemFactory()))
- .setParameterAnnotationsList(
- virtual.getParameterAnnotations().withFakeThisParameter())
- // TODO(b/183998768): Once R8 desugars in the enqueuer this should set an invalid
- // code to ensure it is never used before desugared and installed.
- .setCode(
- ignored ->
- appView.enableWholeProgramOptimizations()
- ? virtual.getCode()
- : InvalidCode.getInstance());
- });
- }
-
- ProgramMethod ensurePrivateAsMethodOfProgramCompanionClassStub(ProgramMethod method) {
- DexMethod companionMethod =
- privateAsMethodOfCompanionClass(method.getReference(), appView.dexItemFactory());
- DexEncodedMethod definition = method.getDefinition();
- return InterfaceProcessor.ensureCompanionMethod(
- method.getHolder(),
- companionMethod.getName(),
- companionMethod.getProto(),
- appView,
- methodBuilder -> {
- MethodAccessFlags newFlags = definition.getAccessFlags().copy();
- assert newFlags.isPrivate();
- newFlags.promoteToPublic();
- newFlags.promoteToStatic();
- methodBuilder
- .setAccessFlags(newFlags)
- .setGenericSignature(definition.getGenericSignature())
- .setAnnotations(definition.annotations())
- // TODO(b/183998768): Should this not also be updating with a fake 'this'
- .setParameterAnnotationsList(definition.getParameterAnnotations())
- // TODO(b/183998768): Once R8 desugars in the enqueuer this should set an invalid
- // code to ensure it is never used before desugared and installed.
- .setCode(
- ignored ->
- appView.enableWholeProgramOptimizations()
- ? definition.getCode()
- : InvalidCode.getInstance());
- });
- }
-
- // Represent a static interface method as a method of companion class.
- final DexMethod staticAsMethodOfCompanionClass(DexClassAndMethod method) {
- DexItemFactory dexItemFactory = appView.dexItemFactory();
- DexType companionClassType = getCompanionClassType(method.getHolderType(), dexItemFactory);
- DexMethod rewritten = method.getReference().withHolder(companionClassType, dexItemFactory);
- return rewritten;
- }
-
- private static DexMethod instanceAsMethodOfCompanionClass(
- DexMethod method, String prefix, DexItemFactory factory) {
- // Add an implicit argument to represent the receiver.
- DexType[] params = method.proto.parameters.values;
- DexType[] newParams = new DexType[params.length + 1];
- newParams[0] = method.holder;
- System.arraycopy(params, 0, newParams, 1, params.length);
-
- // Add prefix to avoid name conflicts.
- return factory.createMethod(
- getCompanionClassType(method.holder, factory),
- factory.createProto(method.proto.returnType, newParams),
- factory.createString(prefix + method.name.toString()));
- }
-
// It is possible that referenced method actually points to an interface which does
// not define this default methods, but inherits it. We are making our best effort
// to find an appropriate method, but still use the original one in case we fail.
@@ -1306,65 +1116,6 @@
return singleCandidate != null ? singleCandidate : method;
}
- // Represent a default interface method as a method of companion class.
- public static DexMethod defaultAsMethodOfCompanionClass(
- DexMethod method, DexItemFactory factory) {
- return instanceAsMethodOfCompanionClass(method, DEFAULT_METHOD_PREFIX, factory);
- }
-
- // Represent a private instance interface method as a method of companion class.
- static DexMethod privateAsMethodOfCompanionClass(DexMethod method, DexItemFactory factory) {
- // Add an implicit argument to represent the receiver.
- return instanceAsMethodOfCompanionClass(method, PRIVATE_METHOD_PREFIX, factory);
- }
-
- private DexMethod privateAsMethodOfCompanionClass(DexClassAndMethod method) {
- return privateAsMethodOfCompanionClass(method.getReference(), factory);
- }
-
- private static DexClassAndMethod ensureMethodOfClasspathCompanionClassStub(
- DexMethod companionMethodReference, ClasspathOrLibraryClass context, AppView<?> appView) {
- return appView
- .getSyntheticItems()
- .ensureFixedClasspathClassMethod(
- companionMethodReference.getName(),
- companionMethodReference.getProto(),
- SyntheticKind.COMPANION_CLASS,
- context,
- appView,
- classBuilder -> {},
- methodBuilder ->
- methodBuilder
- .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
- .setCode(DexEncodedMethod::buildEmptyThrowingCfCode));
- }
-
- ProgramMethod ensureStaticAsMethodOfProgramCompanionClassStub(ProgramMethod method) {
- DexMethod companionMethodReference = staticAsMethodOfCompanionClass(method);
- DexEncodedMethod definition = method.getDefinition();
- return InterfaceProcessor.ensureCompanionMethod(
- method.getHolder(),
- companionMethodReference.getName(),
- companionMethodReference.getProto(),
- appView,
- methodBuilder -> {
- MethodAccessFlags newFlags = definition.getAccessFlags().copy();
- newFlags.promoteToPublic();
- methodBuilder
- .setAccessFlags(newFlags)
- .setGenericSignature(definition.getGenericSignature())
- .setAnnotations(definition.annotations())
- .setParameterAnnotationsList(definition.getParameterAnnotations())
- // TODO(b/183998768): Once R8 desugars in the enqueuer this should set an invalid
- // code to ensure it is never used before desugared and installed.
- .setCode(
- ignored ->
- appView.enableWholeProgramOptimizations()
- ? definition.getCode()
- : InvalidCode.getInstance());
- });
- }
-
public void finalizeInterfaceMethodRewritingThroughIR(
IRConverter converter, ExecutorService executorService) throws ExecutionException {
SortedProgramMethodSet sortedSynthesizedMethods = SortedProgramMethodSet.create();
@@ -1384,74 +1135,13 @@
}
public InterfaceMethodProcessorFacade getPostProcessingDesugaring(Flavor flavour) {
- return new InterfaceMethodProcessorFacade(appView, flavour, this);
- }
-
- final boolean isDefaultMethod(DexEncodedMethod method) {
- assert !method.accessFlags.isConstructor();
- assert !method.accessFlags.isStatic();
-
- if (method.accessFlags.isAbstract()) {
- return false;
- }
- if (method.accessFlags.isNative()) {
- throw new Unimplemented("Native default interface methods are not yet supported.");
- }
- if (!method.accessFlags.isPublic()) {
- // NOTE: even though the class is allowed to have non-public interface methods
- // with code, for example private methods, all such methods we are aware of are
- // created by the compiler for stateful lambdas and they must be converted into
- // static methods by lambda desugaring by this time.
- throw new Unimplemented("Non public default interface methods are not yet supported.");
- }
- return true;
- }
-
- private Predicate<DexType> getShouldIgnoreFromReportsPredicate(AppView<?> appView) {
- DexItemFactory dexItemFactory = appView.dexItemFactory();
- InternalOptions options = appView.options();
- DexString companionClassNameDescriptorSuffix =
- dexItemFactory.createString(COMPANION_CLASS_NAME_SUFFIX + ";");
-
- return type -> {
- DexString descriptor = type.getDescriptor();
- return appView.rewritePrefix.hasRewrittenType(type, appView)
- || descriptor.endsWith(companionClassNameDescriptorSuffix)
- || emulatedInterfaces.containsValue(type)
- || options.desugaredLibraryConfiguration.getCustomConversions().containsValue(type)
- || appView.getDontWarnConfiguration().matches(type);
- };
- }
-
- private boolean shouldIgnoreFromReports(DexType missing) {
- return shouldIgnoreFromReportsPredicate.test(missing);
- }
-
- void warnMissingInterface(DexClass classToDesugar, DexClass implementing, DexType missing) {
- // We use contains() on non hashed collection, but we know it's a 8 cases collection.
- // j$ interfaces won't be missing, they are in the desugared library.
- if (shouldIgnoreFromReports(missing)) {
- return;
- }
- options.warningMissingInterfaceForDesugar(classToDesugar, implementing, missing);
- }
-
- private void warnMissingType(ProgramMethod context, DexType missing) {
- // Companion/Emulated interface/Conversion classes for desugared library won't be missing,
- // they are in the desugared library.
- if (shouldIgnoreFromReports(missing)) {
- return;
- }
- DexMethod method = appView.graphLens().getOriginalMethodSignature(context.getReference());
- Origin origin = getMethodOrigin(method);
- MethodPosition position = new MethodPosition(method.asMethodReference());
- options.warningMissingTypeForDesugar(origin, position, missing, method);
+ return new InterfaceMethodProcessorFacade(appView, flavour);
}
private Origin getMethodOrigin(DexMethod method) {
DexType holder = method.holder;
- if (isCompanionClassType(holder)) {
- holder = getInterfaceClassType(holder);
+ if (InterfaceDesugaringSyntheticHelper.isCompanionClassType(holder)) {
+ holder = helper.getInterfaceClassType(holder);
}
DexClass clazz = appView.definitionFor(holder);
return clazz == null ? Origin.unknown() : clazz.getOrigin();
@@ -1473,7 +1163,7 @@
DefaultMethodsHelper helper = new DefaultMethodsHelper();
DexClass definedInterface = appView.definitionFor(iface);
if (definedInterface == null) {
- warnMissingInterface(classToDesugar, implementing, iface);
+ this.helper.warnMissingInterface(classToDesugar, implementing, iface);
return helper.wrapInCollection();
}
if (!definedInterface.isInterface()) {
@@ -1511,7 +1201,7 @@
// Add all default methods of this interface.
for (DexEncodedMethod encoded : definedInterface.virtualMethods()) {
- if (isDefaultMethod(encoded)) {
+ if (this.helper.isCompatibleDefaultMethod(encoded)) {
helper.addDefaultMethod(encoded);
}
}
@@ -1519,6 +1209,18 @@
return helper.wrapInCollection();
}
+ private void warnMissingType(ProgramMethod context, DexType missing) {
+ // Companion/Emulated interface/Conversion classes for desugared library won't be missing,
+ // they are in the desugared library.
+ if (helper.shouldIgnoreFromReports(missing)) {
+ return;
+ }
+ DexMethod method = appView.graphLens().getOriginalMethodSignature(context.getReference());
+ Origin origin = getMethodOrigin(method);
+ MethodPosition position = new MethodPosition(method.asMethodReference());
+ options.warningMissingTypeForDesugar(origin, position, missing, method);
+ }
+
public static void reportDependencyEdge(
DexClass dependent, DexClass dependency, AppInfo appInfo) {
assert !dependent.isLibraryClass();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriterFixup.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriterFixup.java
index b3d5f1b..1e4b4dc 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriterFixup.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriterFixup.java
@@ -20,9 +20,6 @@
}
void run() {
- if (graphLens == null) {
- return;
- }
for (DexProgramClass clazz : appView.appInfo().classes()) {
if (clazz.getEnclosingMethodAttribute() != null
&& clazz.getEnclosingMethodAttribute().getEnclosingMethod() != null) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index 2dd65a9..c917520 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -46,6 +46,7 @@
import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
+import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap;
import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
import com.google.common.collect.ImmutableList;
import java.util.ArrayDeque;
@@ -69,13 +70,13 @@
public final class InterfaceProcessor implements InterfaceDesugaringProcessor {
private final AppView<?> appView;
- private final InterfaceMethodRewriter rewriter;
+ private final InterfaceDesugaringSyntheticHelper helper;
private final Map<DexProgramClass, PostProcessingInterfaceInfo> postProcessingInterfaceInfos =
new ConcurrentHashMap<>();
- InterfaceProcessor(AppView<?> appView, InterfaceMethodRewriter rewriter) {
+ InterfaceProcessor(AppView<?> appView) {
this.appView = appView;
- this.rewriter = rewriter;
+ helper = new InterfaceDesugaringSyntheticHelper(appView);
}
@Override
@@ -222,7 +223,7 @@
private void processVirtualInterfaceMethods(DexProgramClass iface) {
for (ProgramMethod method : iface.virtualProgramMethods()) {
DexEncodedMethod virtual = method.getDefinition();
- if (rewriter.isDefaultMethod(virtual)) {
+ if (helper.isCompatibleDefaultMethod(virtual)) {
if (!canMoveToCompanionClass(virtual)) {
throw new CompilationError(
"One or more instruction is preventing default interface "
@@ -237,7 +238,7 @@
iface.origin);
}
// Create a new method in a companion class to represent default method implementation.
- ProgramMethod companion = rewriter.ensureDefaultAsMethodOfProgramCompanionClassStub(method);
+ ProgramMethod companion = helper.ensureDefaultAsMethodOfProgramCompanionClassStub(method);
DexEncodedMethod.setDebugInfoWithFakeThisParameter(
code, companion.getReference().getArity(), appView);
finalizeMoveToCompanionMethod(method, companion);
@@ -270,7 +271,7 @@
+ " is expected to "
+ "either be public or private in "
+ iface.origin;
- companion = rewriter.ensureStaticAsMethodOfProgramCompanionClassStub(method);
+ companion = helper.ensureStaticAsMethodOfProgramCompanionClassStub(method);
} else {
assert definition.isPrivate();
Code code = definition.getCode();
@@ -281,7 +282,7 @@
+ method.getReference().toSourceString(),
iface.origin);
}
- companion = rewriter.ensurePrivateAsMethodOfProgramCompanionClassStub(method);
+ companion = helper.ensurePrivateAsMethodOfProgramCompanionClassStub(method);
DexEncodedMethod.setDebugInfoWithFakeThisParameter(
code, companion.getReference().getArity(), appView);
}
@@ -302,7 +303,11 @@
if (definition.hasClassFileVersion()) {
companion.getDefinition().downgradeClassFileVersion(definition.getClassFileVersion());
}
- companion.getDefinition().setCode(definition.getCode(), appView);
+ companion
+ .getDefinition()
+ .setCode(
+ definition.getCode().getCodeAsInlining(companion.getReference(), method.getReference()),
+ appView);
definition.setCode(InvalidCode.getInstance(), appView);
}
@@ -389,7 +394,7 @@
throw new Unimplemented("Native interface methods are not yet supported.");
}
return method.accessFlags.isStatic()
- && !rewriter.factory.isClassConstructor(method.getReference());
+ && !appView.dexItemFactory().isClassConstructor(method.getReference());
}
private InterfaceProcessorNestedGraphLens postProcessInterfaces() {
@@ -443,10 +448,14 @@
@Override
public void finalizeProcessing(InterfaceProcessingDesugaringEventConsumer eventConsumer) {
InterfaceProcessorNestedGraphLens graphLens = postProcessInterfaces();
- if (appView.enableWholeProgramOptimizations() && graphLens != null) {
- appView.setGraphLens(graphLens);
+ if (graphLens != null) {
+ if (appView.enableWholeProgramOptimizations()) {
+ appView.setGraphLens(graphLens);
+ }
+ new InterfaceMethodRewriterFixup(appView, graphLens).run();
+
+ graphLens.moveToPending();
}
- new InterfaceMethodRewriterFixup(appView, graphLens).run();
}
private PostProcessingInterfaceInfo getPostProcessingInterfaceInfo(DexProgramClass iface) {
@@ -510,9 +519,14 @@
// Specific lens which remaps invocation types to static since all rewrites performed here
// are to static companion methods.
+ // TODO(b/167345026): Remove the use of this lens.
public static class InterfaceProcessorNestedGraphLens extends NestedGraphLens {
private BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod> extraNewMethodSignatures;
+ private BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod>
+ pendingNewMethodSignatures;
+ private BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod>
+ pendingExtraNewMethodSignatures;
public InterfaceProcessorNestedGraphLens(
AppView<?> appView,
@@ -524,6 +538,15 @@
this.extraNewMethodSignatures = extraNewMethodSignatures;
}
+ public void moveToPending() {
+ // These are "pending" and installed in the "toggled" state only.
+ pendingNewMethodSignatures = newMethodSignatures;
+ pendingExtraNewMethodSignatures = extraNewMethodSignatures;
+ // The interface methods do not contribute to renaming lens info anymore, so they are cleared.
+ newMethodSignatures = new EmptyBidirectionalOneToOneMap<>();
+ this.extraNewMethodSignatures = new EmptyBidirectionalOneToOneMap<>();
+ }
+
public static InterfaceProcessorNestedGraphLens find(GraphLens lens) {
if (lens.isInterfaceProcessorLens()) {
return lens.asInterfaceProcessorLens();
@@ -538,10 +561,14 @@
return null;
}
- public void toggleMappingToExtraMethods() {
- BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod> tmp = newMethodSignatures;
- this.newMethodSignatures = extraNewMethodSignatures;
- this.extraNewMethodSignatures = tmp;
+ public void enableMapping() {
+ this.newMethodSignatures = pendingExtraNewMethodSignatures;
+ this.extraNewMethodSignatures = pendingNewMethodSignatures;
+ }
+
+ public void disableMapping() {
+ this.newMethodSignatures = new EmptyBidirectionalOneToOneMap<>();
+ this.extraNewMethodSignatures = new EmptyBidirectionalOneToOneMap<>();
}
public BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod>
@@ -586,8 +613,6 @@
new BidirectionalOneToOneHashMap<>();
public void recordCodeMovedToCompanionClass(DexMethod from, DexMethod to) {
- assert from != to;
- methodMap.put(from, from);
extraNewMethodSignatures.put(from, to);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
index ddb7ff5..efc31d3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
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;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
index 5a30d1d..e58024e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
@@ -12,21 +12,13 @@
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
-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;
-import com.android.tools.r8.ir.analysis.value.SingleValue;
-import com.android.tools.r8.ir.code.Assume;
-import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeCustom;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
-import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.PostOptimization;
import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
@@ -41,8 +33,6 @@
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.Sets;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -55,27 +45,39 @@
// For now, before revisiting methods with more precise argument info, we switch the mode.
// Then, revisiting a target at a certain level will not improve call site information of
// callees in lower levels.
- private enum Mode {
- COLLECT, // Set until the end of the 1st round of IR processing. CallSiteOptimizationInfo will
- // be updated in this mode only.
- REVISIT // Set once the all methods are processed. IRBuilder will add other instructions that
- // reflect collected CallSiteOptimizationInfo.
+ public enum Mode {
+ // Set until the end of the 1st round of IR processing. CallSiteOptimizationInfo will be updated
+ // in this mode only.
+ COLLECT,
+ // Set once the all methods are processed. IRBuilder will add other instructions that reflect
+ // collected CallSiteOptimizationInfo.
+ REVISIT;
+
+ public boolean isRevisit() {
+ return this == REVISIT;
+ }
}
private final AppView<AppInfoWithLiveness> appView;
- private final CallSiteOptimizationOptions options;
+ private final InternalOptions options;
+ private final CallSiteOptimizationOptions optimizationOptions;
private ProgramMethodSet revisitedMethods = null;
private Mode mode = Mode.COLLECT;
public CallSiteOptimizationInfoPropagator(AppView<AppInfoWithLiveness> appView) {
assert appView.enableWholeProgramOptimizations();
this.appView = appView;
- this.options = appView.options().callSiteOptimizationOptions();
+ this.options = appView.options();
+ this.optimizationOptions = appView.options().callSiteOptimizationOptions();
if (Log.isLoggingEnabledFor(CallSiteOptimizationInfoPropagator.class)) {
revisitedMethods = ProgramMethodSet.create();
}
}
+ public Mode getMode() {
+ return mode;
+ }
+
public void logResults() {
assert Log.ENABLED;
if (revisitedMethods != null) {
@@ -189,7 +191,7 @@
return;
}
- if (targets.size() > options.getMaxNumberOfDispatchTargetsBeforeAbandoning()) {
+ if (targets.size() > optimizationOptions.getMaxNumberOfDispatchTargetsBeforeAbandoning()) {
// If the number of targets exceed the threshold, abandon call site optimization for all
// targets.
abandonCallSitePropagation(invoke, resolutionResult, targets, context);
@@ -327,7 +329,9 @@
for (DexProgramClass clazz : appView.appInfo().classes()) {
for (ProgramMethod virtualProgramMethod : clazz.virtualProgramMethods()) {
if (virtualProgramMethod.getDefinition().isNonPrivateVirtualMethod()
- && appView.getKeepInfo().isPinned(virtualProgramMethod.getReference(), appView)) {
+ && appView
+ .getKeepInfo()
+ .isPinned(virtualProgramMethod.getReference(), appView, options)) {
consumer.accept(virtualProgramMethod);
}
}
@@ -375,110 +379,6 @@
return callSiteOptimizationInfo;
}
- // If collected call site optimization info has something useful, e.g., non-null argument,
- // insert corresponding assume instructions for arguments.
- public void applyCallSiteOptimizationInfo(
- IRCode code, CallSiteOptimizationInfo callSiteOptimizationInfo) {
- if (mode != Mode.REVISIT) {
- return;
- }
- // TODO(b/139246447): Assert no BOTTOM left.
- if (!callSiteOptimizationInfo.hasUsefulOptimizationInfo(appView, code.method())) {
- return;
- }
- Set<Value> affectedValues = Sets.newIdentityHashSet();
- List<Assume> assumeInstructions = new LinkedList<>();
- List<Instruction> constants = new LinkedList<>();
- int argumentsSeen = 0;
- InstructionListIterator iterator = code.entryBlock().listIterator(code);
- while (iterator.hasNext()) {
- Instruction instr = iterator.next();
- if (!instr.isArgument()) {
- break;
- }
- argumentsSeen++;
- Value originalArg = instr.asArgument().outValue();
- if (originalArg.hasLocalInfo() || !originalArg.getType().isReferenceType()) {
- continue;
- }
- int argIndex = argumentsSeen - 1;
- AbstractValue abstractValue = callSiteOptimizationInfo.getAbstractArgumentValue(argIndex);
- if (abstractValue.isSingleValue()) {
- assert options.isConstantPropagationEnabled();
- SingleValue singleValue = abstractValue.asSingleValue();
- if (singleValue.isMaterializableInContext(appView, code.context())) {
- Instruction replacement =
- singleValue.createMaterializingInstruction(appView, code, instr);
- replacement.setPosition(instr.getPosition());
- affectedValues.addAll(originalArg.affectedValues());
- originalArg.replaceUsers(replacement.outValue());
- constants.add(replacement);
- continue;
- }
- }
- TypeElement dynamicUpperBoundType =
- callSiteOptimizationInfo.getDynamicUpperBoundType(argIndex);
- if (dynamicUpperBoundType == null) {
- continue;
- }
- if (dynamicUpperBoundType.isDefinitelyNull()) {
- ConstNumber nullInstruction = code.createConstNull();
- nullInstruction.setPosition(instr.getPosition());
- affectedValues.addAll(originalArg.affectedValues());
- originalArg.replaceUsers(nullInstruction.outValue());
- constants.add(nullInstruction);
- continue;
- }
- Value specializedArg;
- if (dynamicUpperBoundType.strictlyLessThan(originalArg.getType(), appView)) {
- specializedArg = code.createValue(originalArg.getType());
- affectedValues.addAll(originalArg.affectedValues());
- originalArg.replaceUsers(specializedArg);
- Assume assumeType =
- Assume.createAssumeDynamicTypeInstruction(
- dynamicUpperBoundType, null, specializedArg, originalArg, instr, appView);
- assumeType.setPosition(instr.getPosition());
- assumeInstructions.add(assumeType);
- } else {
- specializedArg = originalArg;
- }
- assert specializedArg != null && specializedArg.getType().isReferenceType();
- if (dynamicUpperBoundType.isDefinitelyNotNull()) {
- // If we already knew `arg` is never null, e.g., receiver, skip adding non-null.
- if (!specializedArg.getType().isDefinitelyNotNull()) {
- Value nonNullArg =
- code.createValue(specializedArg.getType().asReferenceType().asMeetWithNotNull());
- affectedValues.addAll(specializedArg.affectedValues());
- specializedArg.replaceUsers(nonNullArg);
- Assume assumeNotNull =
- Assume.createAssumeNonNullInstruction(nonNullArg, specializedArg, instr, appView);
- assumeNotNull.setPosition(instr.getPosition());
- assumeInstructions.add(assumeNotNull);
- }
- }
- }
- assert argumentsSeen
- == code.method().getReference().getArity() + (code.method().isStatic() ? 0 : 1)
- : "args: "
- + argumentsSeen
- + " != "
- + "arity: "
- + code.method().getReference().getArity()
- + ", static: "
- + code.method().isStatic();
- // After packed Argument instructions, add Assume and constant instructions.
- assert !iterator.peekPrevious().isArgument();
- iterator.previous();
- assert iterator.peekPrevious().isArgument();
- assumeInstructions.forEach(iterator::add);
- // TODO(b/69963623): Can update method signature and save more on call sites.
- constants.forEach(iterator::add);
-
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
- }
-
@Override
public ProgramMethodSet methodsToRevisit() {
mode = Mode.REVISIT;
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 8c5f439..0a42896 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
@@ -16,8 +16,8 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
import com.android.tools.r8.ir.code.BasicBlock;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
index 55780b6..ca6c387 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.optimize;
-import static com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult.isOverriding;
+import static com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult.isOverriding;
import com.android.tools.r8.graph.AccessControl;
import com.android.tools.r8.graph.AppView;
@@ -11,8 +11,8 @@
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.Assume;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
index c25755c..7534cba 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
@@ -6,8 +6,8 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
index b3dd6b6..28530d3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizer.java
@@ -7,8 +7,8 @@
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
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 e4d045d..fdc9208 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
@@ -19,9 +19,9 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.NestMemberClassAttribute;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.proto.ProtoInliningReasonStrategy;
import com.android.tools.r8.ir.analysis.type.Nullability;
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 17db09c..dc88779 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
@@ -17,8 +17,8 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldResolutionResult;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
@@ -174,10 +174,11 @@
if (lookup.holder.isArrayType()) {
return ConstraintWithTarget.ALWAYS;
}
- ResolutionResult resolutionResult = appView.appInfo().unsafeResolveMethodDueToDexFormat(lookup);
+ MethodResolutionResult resolutionResult =
+ appView.appInfo().unsafeResolveMethodDueToDexFormat(lookup);
DexEncodedMethod target =
singleTargetWhileVerticalClassMerging(
- resolutionResult, context, ResolutionResult::lookupInvokeDirectTarget);
+ resolutionResult, context, MethodResolutionResult::lookupInvokeDirectTarget);
return forResolvedMember(resolutionResult.getInitialResolutionHolder(), context, target);
}
@@ -205,10 +206,11 @@
if (lookup.holder.isArrayType()) {
return ConstraintWithTarget.ALWAYS;
}
- ResolutionResult resolutionResult = appView.appInfo().unsafeResolveMethodDueToDexFormat(lookup);
+ MethodResolutionResult resolutionResult =
+ appView.appInfo().unsafeResolveMethodDueToDexFormat(lookup);
DexEncodedMethod target =
singleTargetWhileVerticalClassMerging(
- resolutionResult, context, ResolutionResult::lookupInvokeStaticTarget);
+ resolutionResult, context, MethodResolutionResult::lookupInvokeStaticTarget);
if (!allowStaticInterfaceMethodCalls && target != null) {
// See b/120121170.
DexClass methodClass = appView.definitionFor(graphLens.lookupType(target.getHolderType()));
@@ -221,9 +223,10 @@
@SuppressWarnings("ConstantConditions")
private DexEncodedMethod singleTargetWhileVerticalClassMerging(
- ResolutionResult resolutionResult,
+ MethodResolutionResult resolutionResult,
ProgramMethod context,
- TriFunction<ResolutionResult, DexProgramClass, AppInfoWithClassHierarchy, DexEncodedMethod>
+ TriFunction<
+ MethodResolutionResult, DexProgramClass, AppInfoWithClassHierarchy, DexEncodedMethod>
lookup) {
if (!resolutionResult.isSingleResolution()) {
return null;
@@ -357,7 +360,7 @@
// Perform resolution and derive inlining constraints based on the accessibility of the
// resolution result.
- ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method, isInterface);
+ MethodResolutionResult resolutionResult = appView.appInfo().resolveMethod(method, isInterface);
if (!resolutionResult.isVirtualTarget()) {
return ConstraintWithTarget.NEVER;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
index 5518ab7..1e5c3ae 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
@@ -4,8 +4,8 @@
package com.android.tools.r8.ir.optimize;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
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 ae48979..9608eed 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
@@ -15,8 +15,8 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
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;
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 191d6af..c07ad6d 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
@@ -1322,7 +1322,7 @@
InterfaceProcessorNestedGraphLens interfaceProcessorLens =
InterfaceProcessorNestedGraphLens.find(appView.graphLens());
if (interfaceProcessorLens != null) {
- interfaceProcessorLens.toggleMappingToExtraMethods();
+ interfaceProcessorLens.enableMapping();
}
for (LongLivedProgramMethodMultisetBuilder outlineMethods : candidateMethodLists) {
@@ -1335,7 +1335,7 @@
// TODO(b/167345026): Remove once default interface methods are desugared prior to the first
// optimization pass.
if (interfaceProcessorLens != null) {
- interfaceProcessorLens.toggleMappingToExtraMethods();
+ interfaceProcessorLens.disableMapping();
}
candidateMethodLists.clear();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
index 5536f0e..a40fca9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
@@ -88,7 +88,7 @@
private void processClasses(DexProgramClass clazz) {
// Switchmap classes are synthetic and have a class initializer.
- if (!clazz.accessFlags.isSynthetic() && !clazz.hasClassInitializer()) {
+ if (!clazz.accessFlags.isSynthetic() || !clazz.hasClassInitializer()) {
return;
}
List<DexEncodedField> switchMapFields = clazz.staticFields().stream()
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 01d23b6..65f0d3f 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
@@ -21,9 +21,9 @@
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.MethodResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -1021,7 +1021,7 @@
// We should not inline a method if the invocation has type interface or virtual and the
// signature of the invocation resolves to a private or static method.
// TODO(b/147212189): Why not inline private methods? If access is permitted it is valid.
- ResolutionResult resolutionResult =
+ MethodResolutionResult resolutionResult =
appView.appInfo().resolveMethodOnClass(callee, eligibleClass);
if (resolutionResult.isSingleResolution()
&& !resolutionResult.getSingleTarget().isNonPrivateVirtualMethod()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
index 8cbade6..822b965 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
@@ -23,8 +23,8 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractTransferFunction;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.FailedTransferFunctionResult;
import com.android.tools.r8.ir.analysis.framework.intraprocedural.TransferFunctionResult;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index eb6014b..a5ed9fb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -37,9 +37,9 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldResolutionResult;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues.EnumStaticFieldValues;
@@ -898,7 +898,8 @@
}
// Perform resolution and derive unboxing constraints based on the accessibility of the
// resolution result.
- ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method, isInterface);
+ MethodResolutionResult resolutionResult =
+ appView.appInfo().resolveMethod(method, isInterface);
if (!resolutionResult.isVirtualTarget()) {
constraint = Constraint.NEVER;
return;
@@ -941,7 +942,7 @@
if (method.holder.isArrayType()) {
return;
}
- ResolutionResult resolutionResult =
+ MethodResolutionResult resolutionResult =
appView.appInfo().unsafeResolveMethodDueToDexFormat(method);
DexEncodedMethod target = resolutionResult.getSingleTarget();
if (target == null || !methodValidator.test(target)) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
index 8530a9d3..a675735 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.ir.optimize.enums.eligibility.Reason;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepInfoCollection;
+import com.android.tools.r8.utils.InternalOptions;
class EnumUnboxingCandidateAnalysis {
@@ -100,8 +101,9 @@
// also kept. This is to allow the keep rule -keepclassmembers to be used on enums while
// enum unboxing can still be performed.
KeepInfoCollection keepInfo = appView.appInfo().getKeepInfo();
- keepInfo.forEachPinnedType(this::removePinnedCandidate);
- keepInfo.forEachPinnedField(field -> removePinnedIfNotHolder(field, field.type));
+ InternalOptions options = appView.options();
+ keepInfo.forEachPinnedType(this::removePinnedCandidate, options);
+ keepInfo.forEachPinnedField(field -> removePinnedIfNotHolder(field, field.type), options);
keepInfo.forEachPinnedMethod(
method -> {
DexProto proto = method.proto;
@@ -109,7 +111,8 @@
for (DexType parameterType : proto.parameters.values) {
removePinnedIfNotHolder(method, parameterType);
}
- });
+ },
+ options);
}
private void removePinnedIfNotHolder(DexMember<?, ?> member, DexType type) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
index 83fdf6a..ed27028 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
@@ -9,12 +9,17 @@
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.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ParameterState;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -193,6 +198,68 @@
return isTop ? CallSiteOptimizationInfo.top() : newCallSiteInfo;
}
+ public static CallSiteOptimizationInfo fromMethodState(
+ AppView<AppInfoWithLiveness> appView,
+ ProgramMethod method,
+ ConcreteMonomorphicMethodState methodState) {
+ boolean allowConstantPropagation =
+ appView.options().callSiteOptimizationOptions().isConstantPropagationEnabled();
+ ConcreteCallSiteOptimizationInfo newCallSiteInfo =
+ new ConcreteCallSiteOptimizationInfo(methodState.size(), allowConstantPropagation);
+ boolean isTop = true;
+ for (int argumentIndex = 0; argumentIndex < methodState.size(); argumentIndex++) {
+ ParameterState parameterState = methodState.getParameterState(argumentIndex);
+ if (parameterState.isUnknown()) {
+ continue;
+ }
+
+ ConcreteParameterState concreteParameterState = parameterState.asConcrete();
+
+ // Constant propagation.
+ if (allowConstantPropagation) {
+ AbstractValue abstractValue = concreteParameterState.getAbstractValue();
+ if (abstractValue.isNonTrivial()) {
+ newCallSiteInfo.constants.put(argumentIndex, abstractValue);
+ isTop = false;
+ }
+ }
+
+ // Type propagation.
+ DexType staticType = method.getDefinition().getArgumentType(argumentIndex);
+ if (staticType.isReferenceType()) {
+ TypeElement staticTypeElement = staticType.toTypeElement(appView);
+ if (staticType.isArrayType()) {
+ Nullability nullability = concreteParameterState.asArrayParameter().getNullability();
+ if (nullability.isDefinitelyNull()) {
+ if (allowConstantPropagation) {
+ newCallSiteInfo.constants.put(
+ argumentIndex, appView.abstractValueFactory().createNullValue());
+ }
+ } else if (nullability.isDefinitelyNotNull()) {
+ newCallSiteInfo.dynamicUpperBoundTypes.put(
+ argumentIndex, staticTypeElement.asArrayType().asDefinitelyNotNull());
+ } else {
+ // Should never happen, since the parameter state is unknown in this case.
+ assert false;
+ }
+ } else if (staticType.isClassType()) {
+ DynamicType dynamicType =
+ method.getDefinition().isInstance() && argumentIndex == 0
+ ? concreteParameterState.asReceiverParameter().getDynamicType()
+ : concreteParameterState.asClassParameter().getDynamicType();
+ if (!dynamicType.isTrivial(staticTypeElement)) {
+ newCallSiteInfo.dynamicUpperBoundTypes.put(
+ argumentIndex, dynamicType.getDynamicUpperBoundType());
+ isTop = false;
+ } else {
+ newCallSiteInfo.dynamicUpperBoundTypes.put(argumentIndex, staticTypeElement);
+ }
+ }
+ }
+ }
+ return isTop ? CallSiteOptimizationInfo.top() : newCallSiteInfo;
+ }
+
@Override
public boolean isConcreteCallSiteOptimizationInfo() {
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index ccb3076..6abfb55 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -52,8 +52,8 @@
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.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.DeterminismAnalysis;
import com.android.tools.r8.ir.analysis.InitializedClassesOnNormalExitAnalysis;
@@ -987,7 +987,7 @@
return false;
}
DexItemFactory dexItemFactory = appView.dexItemFactory();
- ResolutionResult resolutionResult =
+ MethodResolutionResult resolutionResult =
appView
.appInfo()
.resolveMethodOnClass(appView.dexItemFactory().objectMembers.finalize, clazz);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
index 0f81ec0..54184d78 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
@@ -251,6 +251,10 @@
this.classInlinerConstraint = classInlinerConstraint;
}
+ void unsetClassInlinerMethodConstraint() {
+ this.classInlinerConstraint = ClassInlinerMethodConstraint.alwaysFalse();
+ }
+
@Override
public EnumUnboxerMethodClassification getEnumUnboxerMethodClassification() {
return enumUnboxerMethodClassification;
@@ -338,6 +342,10 @@
this.bridgeInfo = bridgeInfo;
}
+ void unsetBridgeInfo() {
+ this.bridgeInfo = null;
+ }
+
@Override
public AbstractValue getAbstractReturnValue() {
return abstractReturnValue;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index b248a6d..fe00440 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -163,12 +163,27 @@
method.getMutableOptimizationInfo().setBridgeInfo(bridgeInfo);
}
+ public void unsetBridgeInfo(DexEncodedMethod method) {
+ if (method.getOptimizationInfo().isMutableOptimizationInfo()) {
+ method.getOptimizationInfo().asMutableMethodOptimizationInfo().unsetBridgeInfo();
+ }
+ }
+
@Override
public void setClassInlinerMethodConstraint(
ProgramMethod method, ClassInlinerMethodConstraint classInlinerConstraint) {
// Ignored.
}
+ public void unsetClassInlinerMethodConstraint(ProgramMethod method) {
+ if (method.getOptimizationInfo().isMutableOptimizationInfo()) {
+ method
+ .getOptimizationInfo()
+ .asMutableMethodOptimizationInfo()
+ .unsetClassInlinerMethodConstraint();
+ }
+ }
+
@Override
public void setEnumUnboxerMethodClassification(
ProgramMethod method, EnumUnboxerMethodClassification enumUnboxerMethodClassification) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java
index 08199da..4b7cffb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java
@@ -183,16 +183,8 @@
}
instructionIterator.removeOrReplaceByDebugLocalRead();
} else if (singleTarget.getReference() == objectsMethods.requireNonNullElseGet) {
- // Optimize Objects.requireNonNullElseGet(null, supplier) into supplier.get().
- if (invoke.hasOutValue()) {
- invoke.outValue().replaceUsers(invoke.getLastArgument(), affectedValues);
- }
- instructionIterator.replaceCurrentInstruction(
- InvokeVirtual.builder()
- .setMethod(dexItemFactory.supplierMembers.get)
- .setOutValue(invoke.outValue())
- .setSingleArgument(invoke.getLastArgument())
- .build());
+ // Don't optimize Objects.requireNonNullElseGet. The result of calling supplier.get() still
+ // needs a null-check, so two invokes will be needed.
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java
index 5f74465..34514e3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/sideeffects/JavaLangObjectsSideEffectCollection.java
@@ -9,7 +9,7 @@
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.graph.ResolutionResult.SingleResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfoLookup.java b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfoLookup.java
index 5ddba94..85fb76a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfoLookup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/assume/AssumeInfoLookup.java
@@ -8,7 +8,7 @@
import com.android.tools.r8.graph.DexClassAndMember;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMember;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfo.AssumeType;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.ProguardMemberRule;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
index f600a41..1f6c00a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
@@ -18,8 +18,8 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -658,7 +658,7 @@
return false;
}
AppInfoWithLiveness appInfo = appView.appInfo();
- ResolutionResult resolutionResult =
+ MethodResolutionResult resolutionResult =
appInfo.unsafeResolveMethodDueToDexFormat(methodReferenced);
DexEncodedMethod methodInvoked =
user.isInvokeDirect()
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
index 09790f1..c3f2bc3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
@@ -10,8 +10,8 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.conversion.IRConverter;
@@ -141,6 +141,7 @@
converter.markProcessed(code, feedback);
// Fixup method optimization info (the method no longer returns a constant).
feedback.unsetAbstractReturnValue(parentMethod.getDefinition());
+ feedback.unsetClassInlinerMethodConstraint(parentMethod);
} else {
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
index db93ced..4ce1fb6 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
@@ -32,6 +32,9 @@
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverterEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryWrapperSynthesizer;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.collections.ImmutableDeque;
import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
import java.util.ArrayList;
@@ -44,17 +47,6 @@
super(appView, holder);
}
- boolean shouldConvert(DexType type, DesugaredLibraryAPIConverter converter, DexMethod method) {
- if (!appView.rewritePrefix.hasRewrittenType(type, appView)) {
- return false;
- }
- if (converter.canConvert(type)) {
- return true;
- }
- converter.reportInvalidInvoke(type, method, "");
- return false;
- }
-
DexType vivifiedTypeFor(DexType type) {
return DesugaredLibraryAPIConverter.vivifiedTypeFor(type, appView);
}
@@ -62,22 +54,25 @@
public static class APIConverterVivifiedWrapperCfCodeProvider
extends DesugaredLibraryAPIConversionCfCodeProvider {
- DexField wrapperField;
- DexMethod forwardMethod;
- DesugaredLibraryAPIConverter converter;
- boolean itfCall;
+ private final DexField wrapperField;
+ private final DexMethod forwardMethod;
+ private final DesugaredLibraryWrapperSynthesizer wrapperSynthesizer;
+ private final boolean itfCall;
+ private final DesugaredLibraryAPIConverterEventConsumer eventConsumer;
public APIConverterVivifiedWrapperCfCodeProvider(
AppView<?> appView,
DexMethod forwardMethod,
DexField wrapperField,
- DesugaredLibraryAPIConverter converter,
- boolean itfCall) {
+ DesugaredLibraryWrapperSynthesizer wrapperSynthesizer,
+ boolean itfCall,
+ DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
super(appView, wrapperField.holder);
this.forwardMethod = forwardMethod;
this.wrapperField = wrapperField;
- this.converter = converter;
+ this.wrapperSynthesizer = wrapperSynthesizer;
this.itfCall = itfCall;
+ this.eventConsumer = eventConsumer;
}
@Override
@@ -94,11 +89,12 @@
DexType[] newParameters = forwardMethod.proto.parameters.values.clone();
for (DexType param : forwardMethod.proto.parameters.values) {
instructions.add(new CfLoad(ValueType.fromDexType(param), stackIndex));
- if (shouldConvert(param, converter, forwardMethod)) {
+ if (wrapperSynthesizer.shouldConvert(param, forwardMethod)) {
instructions.add(
new CfInvoke(
Opcodes.INVOKESTATIC,
- converter.ensureConversionMethod(param, param, vivifiedTypeFor(param)),
+ wrapperSynthesizer.ensureConversionMethod(
+ param, param, vivifiedTypeFor(param), eventConsumer),
false));
newParameters[index - 1] = vivifiedTypeFor(param);
}
@@ -125,12 +121,12 @@
instructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, newForwardMethod, false));
}
- if (shouldConvert(returnType, converter, forwardMethod)) {
+ if (wrapperSynthesizer.shouldConvert(returnType, forwardMethod)) {
instructions.add(
new CfInvoke(
Opcodes.INVOKESTATIC,
- converter.ensureConversionMethod(
- returnType, vivifiedTypeFor(returnType), returnType),
+ wrapperSynthesizer.ensureConversionMethod(
+ returnType, vivifiedTypeFor(returnType), returnType, eventConsumer),
false));
}
if (returnType == factory.voidType) {
@@ -147,21 +143,24 @@
DexField wrapperField;
DexMethod forwardMethod;
- DesugaredLibraryAPIConverter converter;
+ DesugaredLibraryWrapperSynthesizer wrapperSynthesizor;
boolean itfCall;
+ private final DesugaredLibraryAPIConverterEventConsumer eventConsumer;
public APIConverterWrapperCfCodeProvider(
AppView<?> appView,
DexMethod forwardMethod,
DexField wrapperField,
- DesugaredLibraryAPIConverter converter,
- boolean itfCall) {
+ DesugaredLibraryWrapperSynthesizer wrapperSynthesizor,
+ boolean itfCall,
+ DesugaredLibraryAPIConverterEventConsumer eventConsumer) {
// Var wrapperField is null if should forward to receiver.
super(appView, wrapperField == null ? forwardMethod.holder : wrapperField.holder);
this.forwardMethod = forwardMethod;
this.wrapperField = wrapperField;
- this.converter = converter;
+ this.wrapperSynthesizor = wrapperSynthesizor;
this.itfCall = itfCall;
+ this.eventConsumer = eventConsumer;
}
@Override
@@ -181,11 +180,12 @@
int stackIndex = 1;
for (DexType param : forwardMethod.proto.parameters.values) {
instructions.add(new CfLoad(ValueType.fromDexType(param), stackIndex));
- if (shouldConvert(param, converter, forwardMethod)) {
+ if (wrapperSynthesizor.shouldConvert(param, forwardMethod)) {
instructions.add(
new CfInvoke(
Opcodes.INVOKESTATIC,
- converter.ensureConversionMethod(param, vivifiedTypeFor(param), param),
+ wrapperSynthesizor.ensureConversionMethod(
+ param, vivifiedTypeFor(param), param, eventConsumer),
false));
}
if (param == factory.longType || param == factory.doubleType) {
@@ -201,12 +201,12 @@
}
DexType returnType = forwardMethod.proto.returnType;
- if (shouldConvert(returnType, converter, forwardMethod)) {
+ if (wrapperSynthesizor.shouldConvert(returnType, forwardMethod)) {
instructions.add(
new CfInvoke(
Opcodes.INVOKESTATIC,
- converter.ensureConversionMethod(
- returnType, returnType, vivifiedTypeFor(returnType)),
+ wrapperSynthesizor.ensureConversionMethod(
+ returnType, returnType, vivifiedTypeFor(returnType), eventConsumer),
false));
returnType = vivifiedTypeFor(returnType);
}
@@ -282,6 +282,70 @@
}
}
+ public static class APIConversionCfCodeProvider extends SyntheticCfCodeProvider {
+
+ private final CfInvoke initialInvoke;
+ private final DexMethod returnConversion;
+ private final DexMethod[] parameterConversions;
+
+ public APIConversionCfCodeProvider(
+ AppView<?> appView,
+ DexType holder,
+ CfInvoke initialInvoke,
+ DexMethod returnConversion,
+ DexMethod[] parameterConversions) {
+ super(appView, holder);
+ this.initialInvoke = initialInvoke;
+ this.returnConversion = returnConversion;
+ this.parameterConversions = parameterConversions;
+ }
+
+ @Override
+ public CfCode generateCfCode() {
+ DexMethod invokedMethod = initialInvoke.getMethod();
+ DexMethod convertedMethod =
+ DesugaredLibraryAPIConverter.getConvertedAPI(
+ invokedMethod, returnConversion, parameterConversions, appView);
+
+ List<CfInstruction> instructions = new ArrayList<>();
+
+ boolean isStatic = initialInvoke.getOpcode() == Opcodes.INVOKESTATIC;
+ if (!isStatic) {
+ instructions.add(new CfLoad(ValueType.fromDexType(invokedMethod.holder), 0));
+ }
+ int receiverShift = BooleanUtils.intValue(!isStatic);
+ int stackIndex = 0;
+ for (int i = 0; i < invokedMethod.getArity(); i++) {
+ DexType param = invokedMethod.getParameter(i);
+ instructions.add(new CfLoad(ValueType.fromDexType(param), stackIndex + receiverShift));
+ if (parameterConversions[i] != null) {
+ instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, parameterConversions[i], false));
+ }
+ if (param == appView.dexItemFactory().longType
+ || param == appView.dexItemFactory().doubleType) {
+ stackIndex++;
+ }
+ stackIndex++;
+ }
+
+ // Actual call to converted value.
+ instructions.add(
+ new CfInvoke(initialInvoke.getOpcode(), convertedMethod, initialInvoke.isInterface()));
+
+ // Return conversion.
+ if (returnConversion != null) {
+ instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, returnConversion, false));
+ }
+
+ if (invokedMethod.getReturnType().isVoidType()) {
+ instructions.add(new CfReturnVoid());
+ } else {
+ instructions.add(new CfReturn(ValueType.fromDexType(invokedMethod.getReturnType())));
+ }
+ return standardCfCodeFromInstructions(instructions);
+ }
+ }
+
public static class APIConverterConstructorCfCodeProvider extends SyntheticCfCodeProvider {
DexField wrapperField;
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/EmulateInterfaceSyntheticCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/EmulateInterfaceSyntheticCfCodeProvider.java
index 7f26a19..e1ba3b2 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/EmulateInterfaceSyntheticCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/EmulateInterfaceSyntheticCfCodeProvider.java
@@ -36,12 +36,13 @@
private final List<Pair<DexType, DexMethod>> extraDispatchCases;
public EmulateInterfaceSyntheticCfCodeProvider(
+ DexType holder,
DexType interfaceType,
DexMethod companionMethod,
DexMethod libraryMethod,
List<Pair<DexType, DexMethod>> extraDispatchCases,
AppView<?> appView) {
- super(appView, interfaceType);
+ super(appView, holder);
this.interfaceType = interfaceType;
this.companionMethod = companionMethod;
this.libraryMethod = libraryMethod;
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
index 7a4fc78..c8cb4d6 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
@@ -78,7 +78,6 @@
.options()
.reporter
.info(KotlinMetadataDiagnostic.lambdaBackingNotFound(clazz.type, function.getName()));
- return false;
}
return function.rewrite(visitorProvider.get()::visitFunction, backing, appView, namingLens);
}
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 8a4d337..e95253c 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
@@ -54,6 +54,7 @@
boolean keepKotlinMetadata =
KeepClassInfo.isKotlinMetadataClassKept(
appView.dexItemFactory(),
+ appView.options(),
appView.appInfo()::definitionForWithoutExistenceAssert,
enqueuer::getKeepInfo);
// In the first round of tree shaking build up all metadata such that it can be traced later.
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java b/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java
index 256ee1e..065ff92 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParser.java
@@ -25,6 +25,7 @@
private static final String SMAP_IDENTIFIER = "SMAP";
private static final String SMAP_SECTION_START = "*S";
private static final String SMAP_SECTION_KOTLIN_START = SMAP_SECTION_START + " Kotlin";
+ private static final String SMAP_SECTION_KOTLIN_DEBUG_START = SMAP_SECTION_START + " KotlinDebug";
private static final String SMAP_FILES_IDENTIFIER = "*F";
private static final String SMAP_LINES_IDENTIFIER = "*L";
private static final String SMAP_END_IDENTIFIER = "*E";
@@ -84,13 +85,13 @@
readUntil(terminator::equals, linesInBlock, callback);
}
- void readUntil(
+ String readUntil(
Predicate<String> terminator,
int linesInBlock,
ThrowingConsumer<List<String>, KotlinSourceDebugExtensionParserException> callback)
throws IOException, KotlinSourceDebugExtensionParserException {
if (terminator.test(readLine)) {
- return;
+ return readLine;
}
List<String> readStrings = new ArrayList<>();
readStrings.add(readNextLine());
@@ -106,10 +107,11 @@
}
readStrings.add(readNextLine());
}
- if (readStrings.size() > 0 && !terminator.test(readStrings.get(0))) {
+ if (!readStrings.isEmpty() && !terminator.test(readStrings.get(0))) {
throw new KotlinSourceDebugExtensionParserException(
"Block size does not match linesInBlock = " + linesInBlock);
}
+ return readStrings.isEmpty() ? null : readStrings.get(0);
}
@Override
@@ -132,14 +134,17 @@
// + <file_id_i> <file_name_i>
// <path_i>
// *L
- // <from>#<file>,<to>:<debug-line-position>
+ // <from>#<file>,<range>:<debug-line-position>
// <from>#<file>:<debug-line-position>
// *E <-- This is an error in versions prior to kotlin 1.5 and is not present in kotlin 1.5.
// *S KotlinDebug
- // ***
+ // + <file_id_i> <file_name_i>
+ // <path_i>
+ // *L
+ // <from>#<file>,<range>:<debug-line-position>
+ // <from>#<file>:<debug-line-position>
// *E
try (BufferedStringReader reader = new BufferedStringReader(annotationData)) {
- ResultBuilder builder = new ResultBuilder();
// Check for SMAP
if (!reader.readExpectedLine(SMAP_IDENTIFIER)) {
return null;
@@ -147,42 +152,67 @@
if (reader.readUntil(SMAP_SECTION_KOTLIN_START).isEOF()) {
return null;
}
- // At this point we should be parsing a kotlin source debug extension, so we will throw if we
- // read an unexpected line.
- reader.readExpectedLineOrThrow(SMAP_FILES_IDENTIFIER);
- // Iterate over the files section with the format:
- // + <file_number_i> <file_name_i>
- // <file_path_i>
- reader.readUntil(
- SMAP_LINES_IDENTIFIER, 2, block -> addFileToBuilder(block.get(0), block.get(1), builder));
- // Ensure that we read *L.
- if (reader.isEOF()) {
- throw new KotlinSourceDebugExtensionParserException(
- "Unexpected EOF - no debug line positions");
+ StratumBuilder inlineePositions = new StratumBuilder();
+ StratumBuilder calleePositions = new StratumBuilder();
+
+ String terminatedLine = parseStratumContents(reader, inlineePositions);
+
+ // Read callee positions
+ if (terminatedLine.equals(SMAP_END_IDENTIFIER)) {
+ String nextLine = reader.readNextLine();
+ // If we read to the end then there is not KotlinDebug section which translates to no need
+ // for mapping the resulting positions obtained from the inlineePositions.
+ if (reader.isEOF()) {
+ assert nextLine == null;
+ return new Result(inlineePositions.segmentTree, calleePositions.segmentTree);
+ }
+ if (!nextLine.equals(SMAP_SECTION_KOTLIN_DEBUG_START)) {
+ return null;
+ }
+ } else if (!terminatedLine.equals(SMAP_SECTION_KOTLIN_DEBUG_START)) {
+ return null;
}
- // Iterate over the debug line number positions:
- // <from>#<file>,<range>:<debug-line-position>
- // or
- // <from>#<file>:<debug-line-position>
- reader.readUntil(
- line -> line.equals(SMAP_END_IDENTIFIER) || line.startsWith(SMAP_SECTION_START),
- 1,
- block -> addDebugEntryToBuilder(block.get(0), builder));
+ parseStratumContents(reader, calleePositions);
// Ensure that we read the end section and it is terminated.
if (reader.isEOF() && !reader.readLine.equals(SMAP_END_IDENTIFIER)) {
throw new KotlinSourceDebugExtensionParserException(
"Unexpected EOF when parsing SMAP debug entries");
}
-
- return builder.build();
+ return new Result(inlineePositions.segmentTree, calleePositions.segmentTree);
} catch (IOException | KotlinSourceDebugExtensionParserException e) {
return null;
}
}
- private static void addFileToBuilder(String entryLine, String filePath, ResultBuilder builder)
+ private static String parseStratumContents(BufferedStringReader reader, StratumBuilder builder)
+ throws KotlinSourceDebugExtensionParserException, IOException {
+ // At this point we should be parsing a kotlin source debug extension, so we will throw if we
+ // read an unexpected line.
+ reader.readExpectedLineOrThrow(SMAP_FILES_IDENTIFIER);
+ // Iterate over the files section with the format:
+ // + <file_number_i> <file_name_i>
+ // <file_path_i>
+ reader.readUntil(
+ SMAP_LINES_IDENTIFIER, 2, block -> addFileToBuilder(block.get(0), block.get(1), builder));
+
+ // Ensure that we read *L.
+ if (reader.isEOF()) {
+ throw new KotlinSourceDebugExtensionParserException(
+ "Unexpected EOF - no debug line positions");
+ }
+ // Iterate over the debug line number positions:
+ // <from>#<file>,<range>:<debug-line-position>
+ // or
+ // <from>#<file>:<debug-line-position>
+ return reader.readUntil(
+ line -> line.equals(SMAP_END_IDENTIFIER) || line.startsWith(SMAP_SECTION_START),
+ 1,
+ block -> addDebugEntryToBuilder(block.get(0), builder));
+ }
+
+ private static void addFileToBuilder(String entryLine, String filePath, StratumBuilder builder)
throws KotlinSourceDebugExtensionParserException {
// + <file_number_i> <file_name_i>
// <file_path_i>
@@ -221,22 +251,30 @@
return number;
}
- private static void addDebugEntryToBuilder(String debugEntry, ResultBuilder builder)
+ private static void addDebugEntryToBuilder(String debugEntry, StratumBuilder builder)
throws KotlinSourceDebugExtensionParserException {
- // <from>#<file>,<size>:<debug-line-position>
+ // <from>#<file>,<size>:<debug-line-position>,<size>
// or
// <from>#<file>:<debug-line-position>
// All positions should define intervals for mappings.
try {
+ int size = 1;
int targetSplit = debugEntry.indexOf(':');
- int target = Integer.parseInt(debugEntry.substring(targetSplit + 1));
+ int targetRangeStart = targetSplit + 1;
+ int targetRangeSeparator = debugEntry.indexOf(',', targetSplit);
+ int target;
+ if (targetRangeSeparator > -1) {
+ target = Integer.parseInt(debugEntry.substring(targetRangeStart, targetRangeSeparator));
+ size = Integer.parseInt(debugEntry.substring(targetRangeSeparator + 1));
+ } else {
+ target = Integer.parseInt(debugEntry.substring(targetRangeStart));
+ }
String original = debugEntry.substring(0, targetSplit);
int fileIndexSplit = original.indexOf('#');
int originalStart = Integer.parseInt(original.substring(0, fileIndexSplit));
// The range may have a different end than start.
String fileAndEndRange = original.substring(fileIndexSplit + 1);
int endRangeCharPosition = fileAndEndRange.indexOf(',');
- int size = 1;
if (endRangeCharPosition > -1) {
// The file should be at least one number wide.
assert endRangeCharPosition > 0;
@@ -260,29 +298,31 @@
public static class Result {
- private final SegmentTree<Position> segmentTree;
+ private final SegmentTree<Position> inlineePositions;
+ private final SegmentTree<Position> calleePositions;
- public Result(SegmentTree<Position> segmentTree) {
- this.segmentTree = segmentTree;
+ public Result(SegmentTree<Position> inlineePositions, SegmentTree<Position> calleePositions) {
+ this.inlineePositions = inlineePositions;
+ this.calleePositions = calleePositions;
}
- public Map.Entry<Integer, Position> lookup(int point) {
- return segmentTree.findEntry(point);
+ public Map.Entry<Integer, Position> lookupInlinedPosition(int point) {
+ return inlineePositions.findEntry(point);
}
- public int size() {
- return segmentTree.size();
+ public Map.Entry<Integer, Position> lookupCalleePosition(int point) {
+ return calleePositions.findEntry(point);
+ }
+
+ public int inlinePositionsCount() {
+ return inlineePositions.size();
}
}
- public static class ResultBuilder {
+ public static class StratumBuilder {
SegmentTree<Position> segmentTree = new SegmentTree<>(false);
Map<Integer, Source> files = new HashMap<>();
-
- public Result build() {
- return new Result(segmentTree);
- }
}
public static class Source {
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index 6ca2736..7e1c7df 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -10,7 +10,7 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodAccessInfoCollection;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
@@ -360,7 +360,7 @@
return;
}
- ResolutionResult resolutionResult = appView.appInfo().resolveMethodOn(holder, method);
+ MethodResolutionResult resolutionResult = appView.appInfo().resolveMethodOn(holder, method);
if (resolutionResult.isSingleResolution()) {
DexEncodedMethod resolvedMethod = resolutionResult.getSingleTarget();
if (resolvedMethod.getReference() == method) {
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
index c44d311..cc64864 100644
--- a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
@@ -13,7 +13,7 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.naming.ClassNameMinifier.ClassRenaming;
import com.android.tools.r8.naming.FieldNameMinifier.FieldRenaming;
import com.android.tools.r8.naming.MethodNameMinifier.MethodRenaming;
@@ -106,7 +106,7 @@
return true;
}
- ResolutionResult resolution = appView.appInfo().unsafeResolveMethodDueToDexFormat(method);
+ MethodResolutionResult resolution = appView.appInfo().unsafeResolveMethodDueToDexFormat(method);
assert resolution != null;
if (resolution.isSingleResolution()) {
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 74737eb..1d0e10b 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -5,6 +5,9 @@
package com.android.tools.r8.naming;
import static com.android.tools.r8.graph.DexApplication.classesWithDeterministicOrder;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.getInterfaceClassType;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.isCompanionClassType;
import static com.android.tools.r8.utils.IterableUtils.fromMethod;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -21,7 +24,6 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.SubtypingInfo;
-import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
import com.android.tools.r8.naming.ClassNameMinifier.ClassRenaming;
import com.android.tools.r8.naming.FieldNameMinifier.FieldRenaming;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
@@ -350,11 +352,10 @@
Map<DexMethod, DexString> defaultInterfaceMethodImplementationNames) {
// If the class does not resolve, then check if it is a companion class for an interface on
// the class path.
- if (!InterfaceMethodRewriter.isCompanionClassType(type)) {
+ if (!isCompanionClassType(type)) {
return;
}
- DexClass interfaceType =
- appView.definitionFor(InterfaceMethodRewriter.getInterfaceClassType(type, factory));
+ DexClass interfaceType = appView.definitionFor(getInterfaceClassType(type, factory));
if (interfaceType == null || !interfaceType.isClasspathClass()) {
return;
}
@@ -368,7 +369,7 @@
MethodSignature signature = (MethodSignature) naming.getOriginalSignature();
if (signature.name.startsWith(interfaceType.type.toSourceString())) {
DexMethod defaultMethod =
- InterfaceMethodRewriter.defaultAsMethodOfCompanionClass(
+ defaultAsMethodOfCompanionClass(
signature.toUnqualified().toDexMethod(factory, interfaceType.type), factory);
assert defaultMethod.holder == type;
defaultInterfaceMethodImplementationNames.put(
diff --git a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
index dd67f3a..ad17739 100644
--- a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.ir.optimize.MemberPoolCollection.MemberPool;
@@ -91,8 +92,6 @@
private void doPublicize(ProgramDefinition definition) {
definition.getAccessFlags().promoteToPublic();
- keepInfo.mutate(
- keepInfo -> keepInfo.unsetRequireAllowAccessModificationForRepackaging(definition));
}
private void publicizeType(DexType type) {
@@ -104,24 +103,12 @@
}
private void publicizeClass(DexProgramClass clazz) {
- doPublicize(clazz);
+ if (appView.appInfo().isAccessModificationAllowed(clazz)) {
+ doPublicize(clazz);
+ }
// Publicize fields.
- clazz.forEachProgramField(
- field -> {
- DexEncodedField definition = field.getDefinition();
- if (definition.isPublic()) {
- return;
- }
- if (!appView.appInfo().isAccessModificationAllowed(field.getReference())) {
- // TODO(b/131130038): Also do not publicize package-private and protected fields that
- // are kept.
- if (definition.isPrivate()) {
- return;
- }
- }
- doPublicize(field);
- });
+ clazz.forEachProgramField(this::publicizeField);
// Publicize methods.
Set<DexEncodedMethod> privateInstanceMethods = new LinkedHashSet<>();
@@ -145,6 +132,21 @@
}
}
+ private void publicizeField(ProgramField field) {
+ DexEncodedField definition = field.getDefinition();
+ if (definition.isPublic()) {
+ return;
+ }
+ if (!appView.appInfo().isAccessModificationAllowed(field)) {
+ // TODO(b/131130038): Also do not publicize package-private and protected fields that
+ // are kept.
+ if (definition.isPrivate()) {
+ return;
+ }
+ }
+ doPublicize(field);
+ }
+
private boolean publicizeMethod(ProgramMethod method) {
MethodAccessFlags accessFlags = method.getAccessFlags();
if (accessFlags.isPublic()) {
@@ -152,7 +154,7 @@
}
// If this method is mentioned in keep rules, do not transform (rule applications changed).
DexEncodedMethod definition = method.getDefinition();
- if (!appView.appInfo().isAccessModificationAllowed(method.getReference())) {
+ if (!appView.appInfo().isAccessModificationAllowed(method)) {
// TODO(b/131130038): Also do not publicize package-private and protected methods that are
// kept.
if (definition.isPrivate()) {
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 0fdc499..8873add 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
@@ -14,7 +14,7 @@
import com.android.tools.r8.graph.FieldAccessInfo;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import java.util.IdentityHashMap;
import java.util.Map;
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
index 6e1cc11..9d9a731 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
@@ -16,8 +16,8 @@
import com.android.tools.r8.graph.FieldAccessInfoImpl;
import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
import com.android.tools.r8.graph.MethodAccessInfoCollection;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ThreadUtils;
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingUtils.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingUtils.java
index 5a9e20c..745cf99 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingUtils.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingUtils.java
@@ -8,8 +8,8 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
public class MemberRebindingUtils {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
new file mode 100644
index 0000000..00ea1ac
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.conversion.PostMethodProcessor;
+import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+/** Optimization that propagates information about arguments from call sites to method entries. */
+// TODO(b/190154391): Add timing information for performance tracking.
+public class ArgumentPropagator {
+
+ private final AppView<AppInfoWithLiveness> appView;
+
+ /**
+ * Collects information about arguments from call sites, meanwhile pruning redundant information.
+ *
+ * <p>The data held by this instance is incomplete and should not be used for optimization until
+ * processed by {@link ArgumentPropagatorOptimizationInfoPopulator}.
+ */
+ private ArgumentPropagatorCodeScanner codeScanner;
+
+ public ArgumentPropagator(AppView<AppInfoWithLiveness> appView) {
+ assert appView.enableWholeProgramOptimizations();
+ assert appView.options().isOptimizing();
+ assert appView.options().callSiteOptimizationOptions().isEnabled();
+ assert appView
+ .options()
+ .callSiteOptimizationOptions()
+ .isExperimentalArgumentPropagationEnabled();
+ this.appView = appView;
+ }
+
+ /**
+ * Called by {@link IRConverter} *before* the primary optimization pass to setup the scanner for
+ * collecting argument information from the code objects.
+ */
+ public void initializeCodeScanner() {
+ codeScanner = new ArgumentPropagatorCodeScanner(appView);
+
+ // Disable argument propagation for methods that should not be optimized.
+ ImmediateProgramSubtypingInfo immediateSubtypingInfo =
+ ImmediateProgramSubtypingInfo.create(appView);
+ // TODO(b/190154391): Consider computing the strongly connected components and running this in
+ // parallel for each scc.
+ new ArgumentPropagatorUnoptimizableMethods(
+ appView, immediateSubtypingInfo, codeScanner.getMethodStates())
+ .disableArgumentPropagationForUnoptimizableMethods(appView.appInfo().classes());
+ }
+
+ /** Called by {@link IRConverter} prior to finalizing methods. */
+ public void scan(ProgramMethod method, IRCode code, MethodProcessor methodProcessor) {
+ if (codeScanner != null) {
+ // TODO(b/190154391): Do we process synthetic methods using a OneTimeMethodProcessor
+ // during the primary optimization pass?
+ assert methodProcessor.isPrimaryMethodProcessor();
+ codeScanner.scan(method, code);
+ } else {
+ assert !methodProcessor.isPrimaryMethodProcessor();
+ }
+ }
+
+ /**
+ * Called by {@link IRConverter} *after* the primary optimization pass to populate the parameter
+ * optimization info.
+ */
+ public void populateParameterOptimizationInfo(ExecutorService executorService)
+ throws ExecutionException {
+ // Unset the scanner since all code objects have been scanned at this point.
+ assert appView.isAllCodeProcessed();
+ MethodStateCollectionByReference codeScannerResult = codeScanner.getMethodStates();
+ codeScanner = null;
+
+ new ArgumentPropagatorOptimizationInfoPopulator(appView, codeScannerResult)
+ .populateOptimizationInfo(executorService);
+ }
+
+ /** Called by {@link IRConverter} to optimize method definitions. */
+ public void optimizeMethodParameters() {
+ // TODO(b/190154391): Remove parameters with constant values.
+ // TODO(b/190154391): Remove unused parameters by simulating they are constant.
+ // TODO(b/190154391): Strengthen the static type of parameters.
+ // TODO(b/190154391): If we learn that a method returns a constant, then consider changing its
+ // return type to void.
+ }
+
+ /**
+ * Called by {@link IRConverter} to add all methods that require reprocessing to {@param
+ * postMethodProcessorBuilder}.
+ */
+ public void enqueueMethodsForProcessing(PostMethodProcessor.Builder postMethodProcessorBuilder) {
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ clazz.forEachProgramMethodMatching(
+ DexEncodedMethod::hasCode,
+ method -> {
+ CallSiteOptimizationInfo callSiteOptimizationInfo =
+ method.getDefinition().getCallSiteOptimizationInfo();
+ if (callSiteOptimizationInfo.isConcreteCallSiteOptimizationInfo()) {
+ postMethodProcessorBuilder.add(method);
+ }
+ });
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
new file mode 100644
index 0000000..509079e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
@@ -0,0 +1,331 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.code.AliasedValueConfiguration;
+import com.android.tools.r8.ir.code.AssumeAndCheckCastAliasedValueConfiguration;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.InvokeCustom;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteArrayTypeParameterState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteClassTypeParameterState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodStateOrUnknown;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcretePolymorphicMethodState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcretePrimitiveTypeParameterState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteReceiverParameterState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameter;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ParameterState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.UnknownMethodState;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.Iterables;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Analyzes each {@link IRCode} during the primary optimization to collect information about the
+ * arguments passed to method parameters.
+ *
+ * <p>State pruning is applied on-the-fly to avoid storing redundant information.
+ */
+class ArgumentPropagatorCodeScanner {
+
+ private static AliasedValueConfiguration aliasedValueConfiguration =
+ AssumeAndCheckCastAliasedValueConfiguration.getInstance();
+
+ private final AppView<AppInfoWithLiveness> appView;
+
+ /**
+ * Maps each non-interface method to the upper most method in the super class chain with the same
+ * method signature. This only contains an entry for non-private virtual methods that override
+ * another method in the program.
+ */
+ private final Map<DexMethod, DexMethod> classMethodRoots;
+
+ /**
+ * The abstract program state for this optimization. Intuitively maps each parameter to its
+ * abstract value and dynamic type.
+ */
+ private final MethodStateCollectionByReference methodStates;
+
+ ArgumentPropagatorCodeScanner(AppView<AppInfoWithLiveness> appView) {
+ this.appView = appView;
+ this.classMethodRoots = computeClassMethodRoots();
+ this.methodStates = computeInitialMethodStates();
+ }
+
+ private Map<DexMethod, DexMethod> computeClassMethodRoots() {
+ // TODO(b/190154391): Group methods related by overriding to enable more effective pruning.
+ Map<DexMethod, DexMethod> roots = new IdentityHashMap<>();
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ clazz.forEachProgramVirtualMethod(
+ method -> roots.put(method.getReference(), method.getReference()));
+ }
+ return roots;
+ }
+
+ private MethodStateCollectionByReference computeInitialMethodStates() {
+ // TODO(b/190154391): There is no need to track an abstract value for receivers; we only care
+ // about the dynamic type for such parameters. Consider initializing the initial state to have
+ // unknown abstract values for all receivers.
+ return MethodStateCollectionByReference.createConcurrent();
+ }
+
+ MethodStateCollectionByReference getMethodStates() {
+ return methodStates;
+ }
+
+ void scan(ProgramMethod method, IRCode code) {
+ for (Invoke invoke : code.<Invoke>instructions(Instruction::isInvoke)) {
+ if (invoke.isInvokeMethod()) {
+ scan(invoke.asInvokeMethod(), method);
+ } else if (invoke.isInvokeCustom()) {
+ scan(invoke.asInvokeCustom(), method);
+ }
+ }
+ }
+
+ private void scan(InvokeMethod invoke, ProgramMethod context) {
+ List<Value> arguments = invoke.arguments();
+ if (arguments.isEmpty()) {
+ // Nothing to propagate.
+ return;
+ }
+
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+ if (invokedMethod.getHolderType().isArrayType()) {
+ // Nothing to propagate.
+ return;
+ }
+
+ SingleResolutionResult resolutionResult =
+ appView.appInfo().unsafeResolveMethodDueToDexFormat(invokedMethod).asSingleResolution();
+ if (resolutionResult == null) {
+ // Nothing to propagate; the invoke instruction fails.
+ return;
+ }
+
+ // TODO(b/190154391): Also bail out if the method is an unoptimizable program method.
+ if (!resolutionResult.getResolvedHolder().isProgramClass()) {
+ // Nothing to propagate; this could dispatch to a program method, but we cannot optimize
+ // methods that override non-program methods.
+ return;
+ }
+
+ ProgramMethod resolvedMethod = resolutionResult.getResolvedProgramMethod();
+ if (resolvedMethod.getDefinition().isLibraryMethodOverride().isPossiblyTrue()) {
+ assert resolvedMethod.getDefinition().isLibraryMethodOverride().isTrue();
+ // Nothing to propagate; we don't know anything about methods that can be called from outside
+ // the program.
+ return;
+ }
+
+ if (arguments.size() != resolvedMethod.getDefinition().getNumberOfArguments()
+ || invoke.isInvokeStatic() != resolvedMethod.getAccessFlags().isStatic()) {
+ // Nothing to propagate; the invoke instruction fails.
+ return;
+ }
+
+ // Find the method where to store the information about the arguments from this invoke.
+ // If the invoke may dispatch to more than one method, we intentionally do not compute all
+ // possible dispatch targets and propagate the information to these methods (this is expensive).
+ // Instead we record the information in one place and then later propagate the information to
+ // all dispatch targets.
+ DexMethod representativeMethodReference =
+ getRepresentativeForPolymorphicInvokeOrElse(
+ invoke, resolvedMethod, resolvedMethod.getReference());
+ methodStates.addMethodState(
+ appView,
+ representativeMethodReference,
+ () -> computeMethodState(invoke, resolvedMethod, context));
+ }
+
+ private MethodState computeMethodState(
+ InvokeMethod invoke, ProgramMethod resolvedMethod, ProgramMethod context) {
+ // If this invoke may target at most one method, then we compute a state that maps each
+ // parameter to the abstract value and dynamic type provided by this call site. Otherwise, we
+ // compute a polymorphic method state, which includes information about the receiver's dynamic
+ // type bounds.
+ boolean isPolymorphicInvoke =
+ getRepresentativeForPolymorphicInvokeOrElse(invoke, resolvedMethod, null) != null;
+ return isPolymorphicInvoke
+ ? computePolymorphicMethodState(invoke.asInvokeMethodWithReceiver(), context)
+ : computeMonomorphicMethodState(invoke, context);
+ }
+
+ // TODO(b/190154391): Add a strategy that widens the dynamic receiver type to allow easily
+ // experimenting with the performance/size trade-off between precise/imprecise handling of
+ // dynamic dispatch.
+ private MethodState computePolymorphicMethodState(
+ InvokeMethodWithReceiver invoke, ProgramMethod context) {
+ DynamicType dynamicReceiverType = invoke.getReceiver().getDynamicType(appView);
+ ConcretePolymorphicMethodState methodState =
+ new ConcretePolymorphicMethodState(
+ dynamicReceiverType,
+ computeMonomorphicMethodState(invoke, context, dynamicReceiverType));
+ // TODO(b/190154391): If the receiver type is effectively unknown, and the computed monomorphic
+ // method state is also unknown (i.e., we have "unknown receiver type" -> "unknown method
+ // state"), then return the canonicalized UnknownMethodState instance instead.
+ return methodState;
+ }
+
+ private ConcreteMonomorphicMethodStateOrUnknown computeMonomorphicMethodState(
+ InvokeMethod invoke, ProgramMethod context) {
+ return computeMonomorphicMethodState(
+ invoke,
+ context,
+ invoke.isInvokeMethodWithReceiver()
+ ? invoke.getFirstArgument().getDynamicType(appView)
+ : null);
+ }
+
+ private ConcreteMonomorphicMethodStateOrUnknown computeMonomorphicMethodState(
+ InvokeMethod invoke, ProgramMethod context, DynamicType dynamicReceiverType) {
+ List<ParameterState> parameterStates = new ArrayList<>(invoke.arguments().size());
+
+ int argumentIndex = 0;
+ if (invoke.isInvokeMethodWithReceiver()) {
+ assert dynamicReceiverType != null;
+ parameterStates.add(
+ computeParameterStateForReceiver(
+ invoke.asInvokeMethodWithReceiver(), dynamicReceiverType));
+ argumentIndex++;
+ }
+
+ for (; argumentIndex < invoke.arguments().size(); argumentIndex++) {
+ parameterStates.add(
+ computeParameterStateForNonReceiver(
+ invoke, argumentIndex, invoke.getArgument(argumentIndex), context));
+ }
+
+ // If all parameter states are unknown, then return a canonicalized unknown method state that
+ // has this property.
+ if (Iterables.all(parameterStates, ParameterState::isUnknown)) {
+ return MethodState.unknown();
+ }
+
+ return new ConcreteMonomorphicMethodState(parameterStates);
+ }
+
+ // For receivers there is not much point in trying to track an abstract value. Therefore we only
+ // track the dynamic type for receivers.
+ // TODO(b/190154391): Consider validating the above hypothesis by using
+ // computeParameterStateForNonReceiver() for receivers.
+ private ParameterState computeParameterStateForReceiver(
+ InvokeMethodWithReceiver invoke, DynamicType dynamicReceiverType) {
+ ClassTypeElement staticReceiverType =
+ invoke
+ .getInvokedMethod()
+ .getHolderType()
+ .toTypeElement(appView)
+ .asClassType()
+ .asMeetWithNotNull();
+ return dynamicReceiverType.isTrivial(staticReceiverType)
+ ? ParameterState.unknown()
+ : new ConcreteReceiverParameterState(dynamicReceiverType);
+ }
+
+ private ParameterState computeParameterStateForNonReceiver(
+ InvokeMethod invoke, int argumentIndex, Value argument, ProgramMethod context) {
+ Value argumentRoot = argument.getAliasedValue(aliasedValueConfiguration);
+ TypeElement parameterType =
+ invoke
+ .getInvokedMethod()
+ .getArgumentType(argumentIndex, invoke.isInvokeStatic())
+ .toTypeElement(appView);
+
+ // If the value is an argument of the enclosing method, then clearly we have no information
+ // about its abstract value. Instead of treating this as having an unknown runtime value, we
+ // instead record a flow constraint that specifies that all values that flow into the parameter
+ // of this enclosing method also flows into the corresponding parameter of the methods
+ // potentially called from this invoke instruction.
+ if (argumentRoot.isArgument()) {
+ MethodParameter forwardedParameter =
+ new MethodParameter(
+ context.getReference(), argumentRoot.getDefinition().asArgument().getIndex());
+ if (parameterType.isClassType()) {
+ return new ConcreteClassTypeParameterState(forwardedParameter);
+ } else if (parameterType.isArrayType()) {
+ return new ConcreteArrayTypeParameterState(forwardedParameter);
+ } else {
+ assert parameterType.isPrimitiveType();
+ return new ConcretePrimitiveTypeParameterState(forwardedParameter);
+ }
+ }
+
+ // Only track the nullability for array types.
+ if (parameterType.isArrayType()) {
+ Nullability nullability = argument.getType().nullability();
+ return nullability.isMaybeNull()
+ ? ParameterState.unknown()
+ : new ConcreteArrayTypeParameterState(nullability);
+ }
+
+ AbstractValue abstractValue = argument.getAbstractValue(appView, context);
+
+ // For class types, we track both the abstract value and the dynamic type. If both are unknown,
+ // then use UnknownParameterState.
+ if (parameterType.isClassType()) {
+ DynamicType dynamicType = argument.getDynamicType(appView);
+ return abstractValue.isUnknown() && dynamicType.isTrivial(parameterType)
+ ? ParameterState.unknown()
+ : new ConcreteClassTypeParameterState(abstractValue, dynamicType);
+ }
+
+ // For primitive types, we only track the abstract value, thus if the abstract value is unknown,
+ // we use UnknownParameterState.
+ assert parameterType.isPrimitiveType();
+ return abstractValue.isUnknown()
+ ? ParameterState.unknown()
+ : new ConcretePrimitiveTypeParameterState(abstractValue);
+ }
+
+ private DexMethod getRepresentativeForPolymorphicInvokeOrElse(
+ InvokeMethod invoke, ProgramMethod resolvedMethod, DexMethod defaultValue) {
+ DexMethod resolvedMethodReference = resolvedMethod.getReference();
+ if (invoke.isInvokeInterface()) {
+ return resolvedMethodReference;
+ }
+ DexMethod rootMethod = classMethodRoots.get(resolvedMethodReference);
+ if (rootMethod != null) {
+ assert invoke.isInvokeVirtual();
+ return rootMethod;
+ }
+ return defaultValue;
+ }
+
+ private void scan(InvokeCustom invoke, ProgramMethod context) {
+ // If the bootstrap method is program declared it will be called. The call is with runtime
+ // provided arguments so ensure that the argument information is unknown.
+ DexMethodHandle bootstrapMethod = invoke.getCallSite().bootstrapMethod;
+ SingleResolutionResult resolution =
+ appView
+ .appInfo()
+ .resolveMethod(bootstrapMethod.asMethod(), bootstrapMethod.isInterface)
+ .asSingleResolution();
+ if (resolution != null && resolution.getResolvedHolder().isProgramClass()) {
+ methodStates.set(resolution.getResolvedProgramMethod(), UnknownMethodState.get());
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorIROptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorIROptimizer.java
new file mode 100644
index 0000000..6f7ec2c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorIROptimizer.java
@@ -0,0 +1,136 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation;
+
+import com.android.tools.r8.graph.AppView;
+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;
+import com.android.tools.r8.ir.analysis.value.SingleValue;
+import com.android.tools.r8.ir.code.Argument;
+import com.android.tools.r8.ir.code.Assume;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.Sets;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+public class ArgumentPropagatorIROptimizer {
+
+ /**
+ * Applies the (non-trivial) argument information to the given piece of code.
+ *
+ * <p>This involves replacing usages of {@link Argument} instructions by constants, and injecting
+ * {@link Assume} instructions when non-trivial information is known about non-constant arguments
+ * such as their nullability, dynamic type, interval, etc.
+ */
+ public static void optimize(
+ AppView<AppInfoWithLiveness> appView,
+ IRCode code,
+ ConcreteCallSiteOptimizationInfo optimizationInfo) {
+ Set<Value> affectedValues = Sets.newIdentityHashSet();
+ List<Assume> assumeInstructions = new LinkedList<>();
+ List<Instruction> instructionsToAdd = new LinkedList<>();
+ InstructionListIterator iterator = code.entryBlock().listIterator(code);
+ while (iterator.hasNext()) {
+ Argument argument = iterator.next().asArgument();
+ if (argument == null) {
+ break;
+ }
+
+ Value argumentValue = argument.asArgument().outValue();
+ if (argumentValue.hasLocalInfo()) {
+ continue;
+ }
+
+ // If the argument is constant, then materialize the constant and replace all uses of the
+ // argument by the newly materialized constant.
+ // TODO(b/190154391): Constant arguments should instead be removed from the enclosing method
+ // signature, and this should assert that the argument does not have a single materializable
+ // value.
+ AbstractValue abstractValue = optimizationInfo.getAbstractArgumentValue(argument.getIndex());
+ if (abstractValue.isSingleValue()) {
+ assert appView.options().callSiteOptimizationOptions().isConstantPropagationEnabled();
+ SingleValue singleValue = abstractValue.asSingleValue();
+ if (singleValue.isMaterializableInContext(appView, code.context())) {
+ Instruction replacement =
+ singleValue.createMaterializingInstruction(appView, code, argument);
+ replacement.setPosition(argument.getPosition());
+ affectedValues.addAll(argumentValue.affectedValues());
+ argumentValue.replaceUsers(replacement.outValue());
+ instructionsToAdd.add(replacement);
+ continue;
+ }
+ }
+
+ // If a dynamic type is known for the argument, then inject an Assume instruction with the
+ // dynamic type information.
+ // TODO(b/190154391): This should also materialize dynamic lower bound information.
+ // TODO(b/190154391) This should also materialize the nullability of array arguments.
+ if (argumentValue.getType().isReferenceType()) {
+ TypeElement dynamicUpperBoundType =
+ optimizationInfo.getDynamicUpperBoundType(argument.getIndex());
+ if (dynamicUpperBoundType == null) {
+ continue;
+ }
+ if (dynamicUpperBoundType.isDefinitelyNull()) {
+ ConstNumber nullInstruction = code.createConstNull();
+ nullInstruction.setPosition(argument.getPosition());
+ affectedValues.addAll(argumentValue.affectedValues());
+ argumentValue.replaceUsers(nullInstruction.outValue());
+ instructionsToAdd.add(nullInstruction);
+ continue;
+ }
+ Value specializedArg;
+ if (dynamicUpperBoundType.strictlyLessThan(argumentValue.getType(), appView)) {
+ specializedArg = code.createValue(argumentValue.getType());
+ affectedValues.addAll(argumentValue.affectedValues());
+ argumentValue.replaceUsers(specializedArg);
+ Assume assumeType =
+ Assume.createAssumeDynamicTypeInstruction(
+ dynamicUpperBoundType, null, specializedArg, argumentValue, argument, appView);
+ assumeType.setPosition(argument.getPosition());
+ assumeInstructions.add(assumeType);
+ } else {
+ specializedArg = argumentValue;
+ }
+ assert specializedArg != null && specializedArg.getType().isReferenceType();
+ if (dynamicUpperBoundType.isDefinitelyNotNull()) {
+ // If we already knew `arg` is never null, e.g., receiver, skip adding non-null.
+ if (!specializedArg.getType().isDefinitelyNotNull()) {
+ Value nonNullArg =
+ code.createValue(specializedArg.getType().asReferenceType().asMeetWithNotNull());
+ affectedValues.addAll(specializedArg.affectedValues());
+ specializedArg.replaceUsers(nonNullArg);
+ Assume assumeNotNull =
+ Assume.createAssumeNonNullInstruction(
+ nonNullArg, specializedArg, argument, appView);
+ assumeNotNull.setPosition(argument.getPosition());
+ assumeInstructions.add(assumeNotNull);
+ }
+ }
+ }
+ }
+
+ // Insert the newly created instructions after the last Argument instruction.
+ assert !iterator.peekPrevious().isArgument();
+ iterator.previous();
+ assert iterator.peekPrevious().isArgument();
+ assumeInstructions.forEach(iterator::add);
+
+ // TODO(b/190154391): Can update method signature and save more on call sites.
+ instructionsToAdd.forEach(iterator::add);
+
+ if (!affectedValues.isEmpty()) {
+ new TypeAnalysis(appView).narrowing(affectedValues);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
new file mode 100644
index 0000000..13455ba
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
@@ -0,0 +1,181 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMethodState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ParameterState;
+import com.android.tools.r8.optimize.argumentpropagation.propagation.InParameterFlowPropagator;
+import com.android.tools.r8.optimize.argumentpropagation.propagation.InterfaceMethodArgumentPropagator;
+import com.android.tools.r8.optimize.argumentpropagation.propagation.VirtualDispatchMethodArgumentPropagator;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.WorkList;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+/**
+ * Propagates the argument flow information collected by the {@link ArgumentPropagatorCodeScanner}.
+ * This is needed to propagate argument information from call sites to all possible dispatch
+ * targets.
+ */
+public class ArgumentPropagatorOptimizationInfoPopulator {
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final MethodStateCollectionByReference methodStates;
+
+ private final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
+ private final List<Set<DexProgramClass>> stronglyConnectedComponents;
+
+ ArgumentPropagatorOptimizationInfoPopulator(
+ AppView<AppInfoWithLiveness> appView, MethodStateCollectionByReference methodStates) {
+ this.appView = appView;
+ this.methodStates = methodStates;
+
+ ImmediateProgramSubtypingInfo immediateSubtypingInfo =
+ ImmediateProgramSubtypingInfo.create(appView);
+ this.immediateSubtypingInfo = immediateSubtypingInfo;
+ this.stronglyConnectedComponents =
+ computeStronglyConnectedComponents(appView, immediateSubtypingInfo);
+ }
+
+ /**
+ * Computes the strongly connected components in the program class hierarchy (where extends and
+ * implements edges are treated as bidirectional).
+ *
+ * <p>All strongly connected components can be processed in parallel.
+ */
+ private static List<Set<DexProgramClass>> computeStronglyConnectedComponents(
+ AppView<AppInfoWithLiveness> appView, ImmediateProgramSubtypingInfo immediateSubtypingInfo) {
+ Set<DexProgramClass> seen = Sets.newIdentityHashSet();
+ List<Set<DexProgramClass>> stronglyConnectedComponents = new ArrayList<>();
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (seen.contains(clazz)) {
+ continue;
+ }
+ Set<DexProgramClass> stronglyConnectedComponent =
+ computeStronglyConnectedComponent(clazz, immediateSubtypingInfo);
+ stronglyConnectedComponents.add(stronglyConnectedComponent);
+ seen.addAll(stronglyConnectedComponent);
+ }
+ return stronglyConnectedComponents;
+ }
+
+ private static Set<DexProgramClass> computeStronglyConnectedComponent(
+ DexProgramClass clazz, ImmediateProgramSubtypingInfo immediateSubtypingInfo) {
+ WorkList<DexProgramClass> worklist = WorkList.newIdentityWorkList(clazz);
+ while (worklist.hasNext()) {
+ DexProgramClass current = worklist.next();
+ immediateSubtypingInfo.forEachImmediateSuperClassMatching(
+ current,
+ (supertype, superclass) -> superclass != null && superclass.isProgramClass(),
+ (supertype, superclass) -> worklist.addIfNotSeen(superclass.asProgramClass()));
+ worklist.addIfNotSeen(immediateSubtypingInfo.getSubclasses(current));
+ }
+ return worklist.getSeenSet();
+ }
+
+ /**
+ * Computes an over-approximation of each parameter's value and type and stores the result in
+ * {@link com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo}.
+ */
+ void populateOptimizationInfo(ExecutorService executorService) throws ExecutionException {
+ // TODO(b/190154391): Propagate argument information to handle virtual dispatch.
+ // TODO(b/190154391): To deal with arguments that are themselves passed as arguments to invoke
+ // instructions, build a flow graph where nodes are parameters and there is an edge from a
+ // parameter p1 to p2 if the value of p2 is at least the value of p1. Then propagate the
+ // collected argument information throughout the flow graph.
+ // TODO(b/190154391): If we learn that parameter p1 is constant, and that the enclosing method
+ // returns p1 according to the optimization info, then update the optimization info to describe
+ // that the method returns the constant.
+ ThreadUtils.processItems(
+ stronglyConnectedComponents, this::processStronglyConnectedComponent, executorService);
+
+ // Solve the parameter flow constraints.
+ new InParameterFlowPropagator(appView, methodStates).run(executorService);
+
+ // The information stored on each method is now sound, and can be used as optimization info.
+ setOptimizationInfo(executorService);
+
+ assert methodStates.isEmpty();
+ }
+
+ private void processStronglyConnectedComponent(Set<DexProgramClass> stronglyConnectedComponent) {
+ // Invoke instructions that target interface methods may dispatch to methods that are not
+ // defined on a subclass of the interface method holder.
+ //
+ // Example: Calling I.m() will dispatch to A.m(), but A is not a subtype of I.
+ //
+ // class A { public void m() {} }
+ // interface I { void m(); }
+ // class B extends A implements I {}
+ //
+ // To handle this we first propagate any argument information stored for I.m() to A.m() by doing
+ // a top-down traversal over the interfaces in the strongly connected component.
+ new InterfaceMethodArgumentPropagator(appView, immediateSubtypingInfo, methodStates)
+ .run(stronglyConnectedComponent);
+
+ // Now all the argument information for a given method is guaranteed to be stored on a supertype
+ // of the method's holder. All that remains is to propagate the information downwards in the
+ // class hierarchy to propagate the argument information for a non-private virtual method to its
+ // overrides.
+ new VirtualDispatchMethodArgumentPropagator(appView, immediateSubtypingInfo, methodStates)
+ .run(stronglyConnectedComponent);
+ }
+
+ private void setOptimizationInfo(ExecutorService executorService) throws ExecutionException {
+ ThreadUtils.processItems(
+ appView.appInfo().classes(), this::setOptimizationInfo, executorService);
+ }
+
+ private void setOptimizationInfo(DexProgramClass clazz) {
+ clazz.forEachProgramMethod(this::setOptimizationInfo);
+ }
+
+ private void setOptimizationInfo(ProgramMethod method) {
+ MethodState methodState = methodStates.remove(method);
+ if (methodState.isBottom()) {
+ // TODO(b/190154391): This should only happen if the method is never called. Consider removing
+ // the method in this case.
+ return;
+ }
+
+ if (methodState.isUnknown()) {
+ // Nothing is known about the arguments to this method.
+ return;
+ }
+
+ ConcreteMethodState concreteMethodState = methodState.asConcrete();
+ if (concreteMethodState.isPolymorphic()) {
+ assert false;
+ return;
+ }
+
+ ConcreteMonomorphicMethodState monomorphicMethodState = concreteMethodState.asMonomorphic();
+ assert monomorphicMethodState.getParameterStates().stream()
+ .filter(ParameterState::isConcrete)
+ .map(ParameterState::asConcrete)
+ .noneMatch(ConcreteParameterState::hasInParameters);
+
+ method
+ .getDefinition()
+ .joinCallSiteOptimizationInfo(
+ ConcreteCallSiteOptimizationInfo.fromMethodState(
+ appView, method, monomorphicMethodState),
+ appView);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorUnoptimizableMethods.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorUnoptimizableMethods.java
new file mode 100644
index 0000000..9a478bd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorUnoptimizableMethods.java
@@ -0,0 +1,245 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation;
+
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.UnknownMethodState;
+import com.android.tools.r8.optimize.argumentpropagation.utils.DepthFirstTopDownClassHierarchyTraversal;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.android.tools.r8.utils.collections.DexMethodSignatureSet;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.Sets;
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+
+public class ArgumentPropagatorUnoptimizableMethods {
+
+ private static final MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
+ private final MethodStateCollectionByReference methodStates;
+
+ public ArgumentPropagatorUnoptimizableMethods(
+ AppView<AppInfoWithLiveness> appView,
+ ImmediateProgramSubtypingInfo immediateSubtypingInfo,
+ MethodStateCollectionByReference methodStates) {
+ this.appView = appView;
+ this.immediateSubtypingInfo = immediateSubtypingInfo;
+ this.methodStates = methodStates;
+ }
+
+ // TODO(b/190154391): Consider if we should bail out for classes that inherit from a missing
+ // class.
+ public void disableArgumentPropagationForUnoptimizableMethods(
+ Collection<DexProgramClass> stronglyConnectedComponent) {
+ ProgramMethodSet unoptimizableClassRootMethods = ProgramMethodSet.create();
+ ProgramMethodSet unoptimizableInterfaceRootMethods = ProgramMethodSet.create();
+ forEachUnoptimizableMethod(
+ stronglyConnectedComponent,
+ method -> {
+ if (method.getDefinition().belongsToVirtualPool()
+ && !method.getHolder().isFinal()
+ && !method.getAccessFlags().isFinal()) {
+ if (method.getHolder().isInterface()) {
+ unoptimizableInterfaceRootMethods.add(method);
+ } else {
+ unoptimizableClassRootMethods.add(method);
+ }
+ } else {
+ disableArgumentPropagationForMethod(method);
+ }
+ });
+
+ // Disable argument propagation for all overrides of the root methods. Since interface methods
+ // may be implemented by classes that are not a subtype of the interface that declares the
+ // interface method, we first mark the interface method overrides on such classes as ineligible
+ // for argument propagation.
+ if (!unoptimizableInterfaceRootMethods.isEmpty()) {
+ new UnoptimizableInterfaceMethodPropagator(
+ unoptimizableClassRootMethods, unoptimizableInterfaceRootMethods)
+ .run(stronglyConnectedComponent);
+ }
+
+ // At this point we can mark all overrides by a simple top-down traversal over the class
+ // hierarchy.
+ new UnoptimizableClassMethodPropagator(
+ unoptimizableClassRootMethods, unoptimizableInterfaceRootMethods)
+ .run(stronglyConnectedComponent);
+ }
+
+ private void disableArgumentPropagationForMethod(ProgramMethod method) {
+ methodStates.set(method, UnknownMethodState.get());
+ }
+
+ private void forEachUnoptimizableMethod(
+ Collection<DexProgramClass> stronglyConnectedComponent, Consumer<ProgramMethod> consumer) {
+ AppInfoWithLiveness appInfo = appView.appInfo();
+ InternalOptions options = appView.options();
+ for (DexProgramClass clazz : stronglyConnectedComponent) {
+ clazz.forEachProgramMethod(
+ method -> {
+ assert !method.getDefinition().isLibraryMethodOverride().isUnknown();
+ if (method.getDefinition().isLibraryMethodOverride().isPossiblyTrue()
+ || appInfo.isMethodTargetedByInvokeDynamic(method)
+ || !appInfo
+ .getKeepInfo()
+ .getMethodInfo(method)
+ .isArgumentPropagationAllowed(options)) {
+ consumer.accept(method);
+ }
+ });
+ }
+ }
+
+ private class UnoptimizableInterfaceMethodPropagator
+ extends DepthFirstTopDownClassHierarchyTraversal {
+
+ private final ProgramMethodSet unoptimizableClassRootMethods;
+ private final Map<DexProgramClass, Set<Wrapper<DexMethod>>> unoptimizableInterfaceMethods =
+ new IdentityHashMap<>();
+
+ UnoptimizableInterfaceMethodPropagator(
+ ProgramMethodSet unoptimizableClassRootMethods,
+ ProgramMethodSet unoptimizableInterfaceRootMethods) {
+ super(
+ ArgumentPropagatorUnoptimizableMethods.this.appView,
+ ArgumentPropagatorUnoptimizableMethods.this.immediateSubtypingInfo);
+ this.unoptimizableClassRootMethods = unoptimizableClassRootMethods;
+ unoptimizableInterfaceRootMethods.forEach(this::addUnoptimizableRootMethod);
+ }
+
+ private void addUnoptimizableRootMethod(ProgramMethod method) {
+ unoptimizableInterfaceMethods
+ .computeIfAbsent(method.getHolder(), ignoreKey(Sets::newIdentityHashSet))
+ .add(equivalence.wrap(method.getReference()));
+ }
+
+ @Override
+ public void visit(DexProgramClass clazz) {
+ Set<Wrapper<DexMethod>> unoptimizableInterfaceMethodsForClass =
+ unoptimizableInterfaceMethods.computeIfAbsent(clazz, ignoreKey(Sets::newIdentityHashSet));
+
+ // Add the unoptimizable interface methods from the parent interfaces.
+ immediateSubtypingInfo.forEachImmediateSuperClassMatching(
+ clazz,
+ (supertype, superclass) -> superclass != null && superclass.isProgramClass(),
+ (supertype, superclass) ->
+ unoptimizableInterfaceMethodsForClass.addAll(
+ unoptimizableInterfaceMethods.get(superclass.asProgramClass())));
+
+ // Propagate the unoptimizable interface methods of this interface to all immediate
+ // (non-interface) subclasses.
+ for (DexProgramClass implementer : immediateSubtypingInfo.getSubclasses(clazz)) {
+ if (implementer.isInterface()) {
+ continue;
+ }
+
+ for (Wrapper<DexMethod> unoptimizableInterfaceMethod :
+ unoptimizableInterfaceMethodsForClass) {
+ SingleResolutionResult resolutionResult =
+ appView
+ .appInfo()
+ .resolveMethodOnClass(unoptimizableInterfaceMethod.get(), implementer)
+ .asSingleResolution();
+ if (resolutionResult == null || !resolutionResult.getResolvedHolder().isProgramClass()) {
+ continue;
+ }
+
+ ProgramMethod resolvedMethod = resolutionResult.getResolvedProgramMethod();
+ if (resolvedMethod.getHolder().isInterface()
+ || resolvedMethod.getHolder() == implementer) {
+ continue;
+ }
+
+ unoptimizableClassRootMethods.add(resolvedMethod);
+ }
+ }
+ }
+
+ @Override
+ public void prune(DexProgramClass clazz) {
+ unoptimizableInterfaceMethods.remove(clazz);
+ }
+
+ @Override
+ public boolean isRoot(DexProgramClass clazz) {
+ return clazz.isInterface() && super.isRoot(clazz);
+ }
+
+ @Override
+ public void forEachSubClass(DexProgramClass clazz, Consumer<DexProgramClass> consumer) {
+ for (DexProgramClass subclass : immediateSubtypingInfo.getSubclasses(clazz)) {
+ if (subclass.isInterface()) {
+ consumer.accept(subclass);
+ }
+ }
+ }
+ }
+
+ private class UnoptimizableClassMethodPropagator
+ extends DepthFirstTopDownClassHierarchyTraversal {
+
+ private final Map<DexProgramClass, DexMethodSignatureSet> unoptimizableMethods =
+ new IdentityHashMap<>();
+
+ UnoptimizableClassMethodPropagator(
+ ProgramMethodSet unoptimizableClassRootMethods,
+ ProgramMethodSet unoptimizableInterfaceRootMethods) {
+ super(
+ ArgumentPropagatorUnoptimizableMethods.this.appView,
+ ArgumentPropagatorUnoptimizableMethods.this.immediateSubtypingInfo);
+ unoptimizableClassRootMethods.forEach(this::addUnoptimizableRootMethod);
+ unoptimizableInterfaceRootMethods.forEach(this::addUnoptimizableRootMethod);
+ }
+
+ private void addUnoptimizableRootMethod(ProgramMethod method) {
+ unoptimizableMethods
+ .computeIfAbsent(method.getHolder(), ignoreKey(DexMethodSignatureSet::create))
+ .add(method);
+ }
+
+ @Override
+ public void visit(DexProgramClass clazz) {
+ DexMethodSignatureSet unoptimizableMethodsForClass =
+ unoptimizableMethods.computeIfAbsent(clazz, ignoreKey(DexMethodSignatureSet::create));
+
+ // Add the unoptimizable methods from the parent classes.
+ immediateSubtypingInfo.forEachImmediateSuperClassMatching(
+ clazz,
+ (supertype, superclass) -> superclass != null && superclass.isProgramClass(),
+ (supertype, superclass) ->
+ unoptimizableMethodsForClass.addAll(
+ unoptimizableMethods.get(superclass.asProgramClass())));
+
+ // Disable argument propagation for the unoptimizable methods of this class.
+ clazz.forEachProgramVirtualMethod(
+ method -> {
+ if (unoptimizableMethodsForClass.contains(method)) {
+ disableArgumentPropagationForMethod(method);
+ }
+ });
+ }
+
+ @Override
+ public void prune(DexProgramClass clazz) {
+ unoptimizableMethods.remove(clazz);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomMethodState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomMethodState.java
new file mode 100644
index 0000000..fea97a2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomMethodState.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.codescanner;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.function.Supplier;
+
+public class BottomMethodState extends MethodStateBase {
+
+ private static final BottomMethodState INSTANCE = new BottomMethodState();
+
+ private BottomMethodState() {}
+
+ public static BottomMethodState get() {
+ return INSTANCE;
+ }
+
+ @Override
+ public boolean isBottom() {
+ return true;
+ }
+
+ @Override
+ public MethodState mutableJoin(AppView<AppInfoWithLiveness> appView, MethodState methodState) {
+ return methodState;
+ }
+
+ @Override
+ public MethodState mutableJoin(
+ AppView<AppInfoWithLiveness> appView, Supplier<MethodState> methodStateSupplier) {
+ return methodStateSupplier.get();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteArrayTypeParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteArrayTypeParameterState.java
new file mode 100644
index 0000000..e7533e5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteArrayTypeParameterState.java
@@ -0,0 +1,62 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.codescanner;
+
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.utils.Action;
+
+public class ConcreteArrayTypeParameterState extends ConcreteParameterState {
+
+ private Nullability nullability;
+
+ public ConcreteArrayTypeParameterState(MethodParameter inParameter) {
+ super(inParameter);
+ this.nullability = Nullability.bottom();
+ }
+
+ public ConcreteArrayTypeParameterState(Nullability nullability) {
+ assert !nullability.isMaybeNull() : "Must use UnknownParameterState instead";
+ this.nullability = nullability;
+ }
+
+ public ParameterState mutableJoin(
+ ConcreteArrayTypeParameterState parameterState, Action onChangedAction) {
+ assert !nullability.isMaybeNull();
+ assert !parameterState.nullability.isMaybeNull();
+ boolean inParametersChanged = mutableJoinInParameters(parameterState);
+ if (widenInParameters()) {
+ return unknown();
+ }
+ if (inParametersChanged) {
+ onChangedAction.execute();
+ }
+ return this;
+ }
+
+ @Override
+ public AbstractValue getAbstractValue() {
+ return AbstractValue.unknown();
+ }
+
+ @Override
+ public ConcreteParameterStateKind getKind() {
+ return ConcreteParameterStateKind.ARRAY;
+ }
+
+ public Nullability getNullability() {
+ return nullability;
+ }
+
+ @Override
+ public boolean isArrayParameter() {
+ return true;
+ }
+
+ @Override
+ public ConcreteArrayTypeParameterState asArrayParameter() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java
new file mode 100644
index 0000000..3c59a07
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.codescanner;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Action;
+
+public class ConcreteClassTypeParameterState extends ConcreteParameterState {
+
+ private AbstractValue abstractValue;
+ private DynamicType dynamicType;
+
+ public ConcreteClassTypeParameterState(MethodParameter inParameter) {
+ super(inParameter);
+ this.abstractValue = AbstractValue.bottom();
+ this.dynamicType = DynamicType.bottom();
+ }
+
+ public ConcreteClassTypeParameterState(AbstractValue abstractValue, DynamicType dynamicType) {
+ this.abstractValue = abstractValue;
+ this.dynamicType = dynamicType;
+ }
+
+ public ParameterState mutableJoin(
+ AppView<AppInfoWithLiveness> appView,
+ ConcreteClassTypeParameterState parameterState,
+ Action onChangedAction) {
+ boolean allowNullOrAbstractValue = true;
+ boolean allowNonConstantNumbers = false;
+ AbstractValue oldAbstractValue = abstractValue;
+ abstractValue =
+ abstractValue.join(
+ parameterState.abstractValue,
+ appView.abstractValueFactory(),
+ allowNullOrAbstractValue,
+ allowNonConstantNumbers);
+ // TODO(b/190154391): Join the dynamic types using SubtypingInfo.
+ // TODO(b/190154391): Take in the static type as an argument, and unset the dynamic type if it
+ // equals the static type.
+ DynamicType oldDynamicType = dynamicType;
+ dynamicType =
+ dynamicType.equals(parameterState.dynamicType) ? dynamicType : DynamicType.unknown();
+ if (abstractValue.isUnknown() && dynamicType.isUnknown()) {
+ return unknown();
+ }
+ boolean inParametersChanged = mutableJoinInParameters(parameterState);
+ if (widenInParameters()) {
+ return unknown();
+ }
+ if (abstractValue != oldAbstractValue || dynamicType != oldDynamicType || inParametersChanged) {
+ onChangedAction.execute();
+ }
+ return this;
+ }
+
+ @Override
+ public AbstractValue getAbstractValue() {
+ return abstractValue;
+ }
+
+ public DynamicType getDynamicType() {
+ return dynamicType;
+ }
+
+ @Override
+ public ConcreteParameterStateKind getKind() {
+ return ConcreteParameterStateKind.CLASS;
+ }
+
+ @Override
+ public boolean isClassParameter() {
+ return true;
+ }
+
+ @Override
+ public ConcreteClassTypeParameterState asClassParameter() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMethodState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMethodState.java
new file mode 100644
index 0000000..6aae75f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMethodState.java
@@ -0,0 +1,59 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.codescanner;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.function.Supplier;
+
+public abstract class ConcreteMethodState extends MethodStateBase {
+
+ @Override
+ public boolean isConcrete() {
+ return true;
+ }
+
+ @Override
+ public ConcreteMethodState asConcrete() {
+ return this;
+ }
+
+ public boolean isPolymorphic() {
+ return false;
+ }
+
+ public ConcretePolymorphicMethodState asPolymorphic() {
+ return null;
+ }
+
+ @Override
+ public MethodState mutableJoin(AppView<AppInfoWithLiveness> appView, MethodState methodState) {
+ if (methodState.isBottom()) {
+ return this;
+ }
+ if (methodState.isUnknown()) {
+ return methodState;
+ }
+ return mutableJoin(appView, methodState.asConcrete());
+ }
+
+ @Override
+ public MethodState mutableJoin(
+ AppView<AppInfoWithLiveness> appView, Supplier<MethodState> methodStateSupplier) {
+ return mutableJoin(appView, methodStateSupplier.get());
+ }
+
+ private MethodState mutableJoin(
+ AppView<AppInfoWithLiveness> appView, ConcreteMethodState methodState) {
+ if (isMonomorphic() && methodState.isMonomorphic()) {
+ return asMonomorphic().mutableJoin(appView, methodState.asMonomorphic());
+ }
+ if (isPolymorphic() && methodState.isPolymorphic()) {
+ return asPolymorphic().mutableJoin(appView, methodState.asPolymorphic());
+ }
+ assert false;
+ return unknown();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodState.java
new file mode 100644
index 0000000..fc8fe25
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodState.java
@@ -0,0 +1,67 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.codescanner;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.Iterables;
+import java.util.List;
+
+public class ConcreteMonomorphicMethodState extends ConcreteMethodState
+ implements ConcreteMonomorphicMethodStateOrUnknown {
+
+ List<ParameterState> parameterStates;
+
+ public ConcreteMonomorphicMethodState(List<ParameterState> parameterStates) {
+ assert Iterables.any(parameterStates, parameterState -> !parameterState.isUnknown())
+ : "Must use UnknownMethodState instead";
+ this.parameterStates = parameterStates;
+ }
+
+ public ParameterState getParameterState(int index) {
+ return parameterStates.get(index);
+ }
+
+ public List<ParameterState> getParameterStates() {
+ return parameterStates;
+ }
+
+ public ConcreteMonomorphicMethodStateOrUnknown mutableJoin(
+ AppView<AppInfoWithLiveness> appView, ConcreteMonomorphicMethodState methodState) {
+ if (size() != methodState.size()) {
+ assert false;
+ return unknown();
+ }
+
+ for (int i = 0; i < size(); i++) {
+ ParameterState parameterState = parameterStates.get(i);
+ ParameterState otherParameterState = methodState.parameterStates.get(i);
+ parameterStates.set(i, parameterState.mutableJoin(appView, otherParameterState));
+ }
+
+ if (Iterables.all(parameterStates, ParameterState::isUnknown)) {
+ return unknown();
+ }
+ return this;
+ }
+
+ @Override
+ public boolean isMonomorphic() {
+ return true;
+ }
+
+ @Override
+ public ConcreteMonomorphicMethodState asMonomorphic() {
+ return this;
+ }
+
+ public void setParameterState(int index, ParameterState parameterState) {
+ parameterStates.set(index, parameterState);
+ }
+
+ public int size() {
+ return parameterStates.size();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodStateOrUnknown.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodStateOrUnknown.java
new file mode 100644
index 0000000..1b90e84
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodStateOrUnknown.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.codescanner;
+
+public interface ConcreteMonomorphicMethodStateOrUnknown extends MethodState {}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteParameterState.java
new file mode 100644
index 0000000..1e5ebae
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteParameterState.java
@@ -0,0 +1,138 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.codescanner;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Action;
+import com.android.tools.r8.utils.SetUtils;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.Set;
+
+public abstract class ConcreteParameterState extends ParameterState {
+
+ enum ConcreteParameterStateKind {
+ ARRAY,
+ CLASS,
+ PRIMITIVE,
+ RECEIVER
+ }
+
+ private Set<MethodParameter> inParameters;
+
+ ConcreteParameterState() {
+ this.inParameters = Collections.emptySet();
+ }
+
+ ConcreteParameterState(MethodParameter inParameter) {
+ this.inParameters = SetUtils.newHashSet(inParameter);
+ }
+
+ public void clearInParameters() {
+ inParameters.clear();
+ }
+
+ public boolean hasInParameters() {
+ return !inParameters.isEmpty();
+ }
+
+ public Set<MethodParameter> getInParameters() {
+ return inParameters;
+ }
+
+ public abstract ConcreteParameterStateKind getKind();
+
+ public boolean isArrayParameter() {
+ return false;
+ }
+
+ public ConcreteArrayTypeParameterState asArrayParameter() {
+ return null;
+ }
+
+ public boolean isClassParameter() {
+ return false;
+ }
+
+ public ConcreteClassTypeParameterState asClassParameter() {
+ return null;
+ }
+
+ public boolean isPrimitiveParameter() {
+ return false;
+ }
+
+ public ConcretePrimitiveTypeParameterState asPrimitiveParameter() {
+ return null;
+ }
+
+ public boolean isReceiverParameter() {
+ return false;
+ }
+
+ public ConcreteReceiverParameterState asReceiverParameter() {
+ return null;
+ }
+
+ @Override
+ public boolean isConcrete() {
+ return true;
+ }
+
+ @Override
+ public ConcreteParameterState asConcrete() {
+ return this;
+ }
+
+ @Override
+ public ParameterState mutableJoin(
+ AppView<AppInfoWithLiveness> appView, ParameterState parameterState, Action onChangedAction) {
+ if (parameterState.isUnknown()) {
+ return parameterState;
+ }
+ ConcreteParameterStateKind kind = getKind();
+ ConcreteParameterStateKind otherKind = parameterState.asConcrete().getKind();
+ if (kind == otherKind) {
+ switch (getKind()) {
+ case ARRAY:
+ return asArrayParameter()
+ .mutableJoin(parameterState.asConcrete().asArrayParameter(), onChangedAction);
+ case CLASS:
+ return asClassParameter()
+ .mutableJoin(
+ appView, parameterState.asConcrete().asClassParameter(), onChangedAction);
+ case PRIMITIVE:
+ return asPrimitiveParameter()
+ .mutableJoin(
+ appView, parameterState.asConcrete().asPrimitiveParameter(), onChangedAction);
+ case RECEIVER:
+ return asReceiverParameter()
+ .mutableJoin(parameterState.asConcrete().asReceiverParameter(), onChangedAction);
+ default:
+ // Dead.
+ }
+ }
+
+ assert false;
+ return unknown();
+ }
+
+ boolean mutableJoinInParameters(ConcreteParameterState parameterState) {
+ if (parameterState.inParameters.isEmpty()) {
+ return false;
+ }
+ if (inParameters.isEmpty()) {
+ assert inParameters == Collections.<MethodParameter>emptySet();
+ inParameters = Sets.newIdentityHashSet();
+ }
+ return inParameters.addAll(parameterState.inParameters);
+ }
+
+ boolean widenInParameters() {
+ // TODO(b/190154391): Widen to unknown when the size of the collection exceeds a threshold.
+ return false;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePolymorphicMethodState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePolymorphicMethodState.java
new file mode 100644
index 0000000..2e5a908
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePolymorphicMethodState.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.codescanner;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+public class ConcretePolymorphicMethodState extends ConcreteMethodState {
+
+ private final Map<DynamicType, ConcreteMonomorphicMethodStateOrUnknown> receiverBoundsToState =
+ new HashMap<>();
+
+ public ConcretePolymorphicMethodState(
+ DynamicType receiverBounds, ConcreteMonomorphicMethodStateOrUnknown methodState) {
+ // TODO(b/190154391): Ensure that we use the unknown state instead of mapping unknown -> unknown
+ // here.
+ receiverBoundsToState.put(receiverBounds, methodState);
+ }
+
+ public void forEach(BiConsumer<DynamicType, MethodState> consumer) {
+ receiverBoundsToState.forEach(consumer);
+ }
+
+ public boolean isEmpty() {
+ return receiverBoundsToState.isEmpty();
+ }
+
+ public MethodState mutableJoin(
+ AppView<AppInfoWithLiveness> appView, ConcretePolymorphicMethodState methodState) {
+ assert !isEmpty();
+ assert !methodState.isEmpty();
+ methodState.receiverBoundsToState.forEach(
+ (receiverBounds, stateToAdd) -> {
+ if (stateToAdd.isUnknown()) {
+ receiverBoundsToState.put(receiverBounds, stateToAdd);
+ } else {
+ assert stateToAdd.isMonomorphic();
+ receiverBoundsToState.compute(
+ receiverBounds,
+ (ignore, existingState) -> {
+ if (existingState == null) {
+ return stateToAdd;
+ }
+ if (existingState.isUnknown()) {
+ return existingState;
+ }
+ assert existingState.isMonomorphic();
+ return existingState
+ .asMonomorphic()
+ .mutableJoin(appView, stateToAdd.asMonomorphic());
+ });
+ }
+ });
+ // TODO(b/190154391): Widen to unknown when the unknown dynamic type is mapped to unknown.
+ return this;
+ }
+
+ @Override
+ public boolean isPolymorphic() {
+ return true;
+ }
+
+ @Override
+ public ConcretePolymorphicMethodState asPolymorphic() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePrimitiveTypeParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePrimitiveTypeParameterState.java
new file mode 100644
index 0000000..0b85fb6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePrimitiveTypeParameterState.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.codescanner;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Action;
+
+public class ConcretePrimitiveTypeParameterState extends ConcreteParameterState {
+
+ private AbstractValue abstractValue;
+
+ public ConcretePrimitiveTypeParameterState(AbstractValue abstractValue) {
+ assert !abstractValue.isUnknown() : "Must use UnknownParameterState";
+ this.abstractValue = abstractValue;
+ }
+
+ public ConcretePrimitiveTypeParameterState(MethodParameter inParameter) {
+ super(inParameter);
+ this.abstractValue = AbstractValue.bottom();
+ }
+
+ public ParameterState mutableJoin(
+ AppView<AppInfoWithLiveness> appView,
+ ConcretePrimitiveTypeParameterState parameterState,
+ Action onChangedAction) {
+ boolean allowNullOrAbstractValue = false;
+ boolean allowNonConstantNumbers = false;
+ AbstractValue oldAbstractValue = abstractValue;
+ abstractValue =
+ abstractValue.join(
+ parameterState.abstractValue,
+ appView.abstractValueFactory(),
+ allowNullOrAbstractValue,
+ allowNonConstantNumbers);
+ if (abstractValue.isUnknown()) {
+ return unknown();
+ }
+ boolean inParametersChanged = mutableJoinInParameters(parameterState);
+ if (widenInParameters()) {
+ return unknown();
+ }
+ if (abstractValue != oldAbstractValue || inParametersChanged) {
+ onChangedAction.execute();
+ }
+ return this;
+ }
+
+ @Override
+ public AbstractValue getAbstractValue() {
+ return abstractValue;
+ }
+
+ @Override
+ public ConcreteParameterStateKind getKind() {
+ return ConcreteParameterStateKind.PRIMITIVE;
+ }
+
+ @Override
+ public boolean isPrimitiveParameter() {
+ return true;
+ }
+
+ @Override
+ public ConcretePrimitiveTypeParameterState asPrimitiveParameter() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReceiverParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReceiverParameterState.java
new file mode 100644
index 0000000..145d29b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteReceiverParameterState.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.codescanner;
+
+import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.utils.Action;
+
+public class ConcreteReceiverParameterState extends ConcreteParameterState {
+
+ private DynamicType dynamicType;
+
+ public ConcreteReceiverParameterState(DynamicType dynamicType) {
+ this.dynamicType = dynamicType;
+ }
+
+ public ParameterState mutableJoin(
+ ConcreteReceiverParameterState parameterState, Action onChangedAction) {
+ // TODO(b/190154391): Join the dynamic types using SubtypingInfo.
+ // TODO(b/190154391): Take in the static type as an argument, and unset the dynamic type if it
+ // equals the static type.
+ DynamicType oldDynamicType = dynamicType;
+ dynamicType =
+ dynamicType.equals(parameterState.dynamicType) ? dynamicType : DynamicType.unknown();
+ if (dynamicType.isUnknown()) {
+ return unknown();
+ }
+ boolean inParametersChanged = mutableJoinInParameters(parameterState);
+ if (widenInParameters()) {
+ return unknown();
+ }
+ if (dynamicType != oldDynamicType || inParametersChanged) {
+ onChangedAction.execute();
+ }
+ return this;
+ }
+
+ @Override
+ public AbstractValue getAbstractValue() {
+ return AbstractValue.unknown();
+ }
+
+ public DynamicType getDynamicType() {
+ return dynamicType;
+ }
+
+ @Override
+ public ConcreteParameterStateKind getKind() {
+ return ConcreteParameterStateKind.RECEIVER;
+ }
+
+ @Override
+ public boolean isReceiverParameter() {
+ return true;
+ }
+
+ @Override
+ public ConcreteReceiverParameterState asReceiverParameter() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodParameter.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodParameter.java
new file mode 100644
index 0000000..9f7434b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodParameter.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.codescanner;
+
+import com.android.tools.r8.graph.DexMethod;
+import java.util.Objects;
+
+public class MethodParameter {
+
+ private final DexMethod method;
+ private final int index;
+
+ public MethodParameter(DexMethod method, int index) {
+ this.method = method;
+ this.index = index;
+ }
+
+ public DexMethod getMethod() {
+ return method;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ MethodParameter methodParameter = (MethodParameter) obj;
+ return method == methodParameter.method && index == methodParameter.index;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, index);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodState.java
new file mode 100644
index 0000000..5bc6049
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodState.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.codescanner;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.function.Supplier;
+
+public interface MethodState {
+
+ static BottomMethodState bottom() {
+ return BottomMethodState.get();
+ }
+
+ static UnknownMethodState unknown() {
+ return UnknownMethodState.get();
+ }
+
+ boolean isBottom();
+
+ boolean isConcrete();
+
+ ConcreteMethodState asConcrete();
+
+ boolean isMonomorphic();
+
+ ConcreteMonomorphicMethodState asMonomorphic();
+
+ boolean isUnknown();
+
+ MethodState mutableJoin(AppView<AppInfoWithLiveness> appView, MethodState methodState);
+
+ MethodState mutableJoin(
+ AppView<AppInfoWithLiveness> appView, Supplier<MethodState> methodStateSupplier);
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateBase.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateBase.java
new file mode 100644
index 0000000..936210f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateBase.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.codescanner;
+
+public abstract class MethodStateBase implements MethodState {
+
+ public static BottomMethodState bottom() {
+ return BottomMethodState.get();
+ }
+
+ public static UnknownMethodState unknown() {
+ return UnknownMethodState.get();
+ }
+
+ @Override
+ public boolean isBottom() {
+ return false;
+ }
+
+ @Override
+ public boolean isConcrete() {
+ return false;
+ }
+
+ @Override
+ public ConcreteMethodState asConcrete() {
+ return null;
+ }
+
+ @Override
+ public boolean isMonomorphic() {
+ return false;
+ }
+
+ @Override
+ public ConcreteMonomorphicMethodState asMonomorphic() {
+ return null;
+ }
+
+ @Override
+ public boolean isUnknown() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollection.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollection.java
new file mode 100644
index 0000000..c8b2456
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollection.java
@@ -0,0 +1,94 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.codescanner;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Supplier;
+
+abstract class MethodStateCollection<K> {
+
+ private final Map<K, MethodState> methodStates;
+
+ MethodStateCollection(Map<K, MethodState> methodStates) {
+ this.methodStates = methodStates;
+ }
+
+ abstract K getKey(ProgramMethod method);
+
+ public void addMethodState(
+ AppView<AppInfoWithLiveness> appView, ProgramMethod method, MethodState methodState) {
+ addMethodState(appView, getKey(method), methodState);
+ }
+
+ private void addMethodState(
+ AppView<AppInfoWithLiveness> appView, K method, MethodState methodState) {
+ if (methodState.isUnknown()) {
+ methodStates.put(method, methodState);
+ } else {
+ methodStates.compute(
+ method,
+ (ignore, existingMethodState) -> {
+ if (existingMethodState == null) {
+ return methodState;
+ }
+ return existingMethodState.mutableJoin(appView, methodState);
+ });
+ }
+ }
+
+ /**
+ * This intentionally takes a {@link Supplier<MethodState>} to avoid computing the method state
+ * for a given call site when nothing is known about the arguments of the method.
+ */
+ public void addMethodState(
+ AppView<AppInfoWithLiveness> appView,
+ ProgramMethod method,
+ Supplier<MethodState> methodStateSupplier) {
+ addMethodState(appView, getKey(method), methodStateSupplier);
+ }
+
+ public void addMethodState(
+ AppView<AppInfoWithLiveness> appView, K method, Supplier<MethodState> methodStateSupplier) {
+ methodStates.compute(
+ method,
+ (ignore, existingMethodState) -> {
+ if (existingMethodState == null) {
+ return methodStateSupplier.get();
+ }
+ return existingMethodState.mutableJoin(appView, methodStateSupplier);
+ });
+ }
+
+ public void addMethodStates(
+ AppView<AppInfoWithLiveness> appView, MethodStateCollection<K> other) {
+ other.methodStates.forEach(
+ (method, methodState) -> addMethodState(appView, method, methodState));
+ }
+
+ public void forEach(BiConsumer<K, MethodState> consumer) {
+ methodStates.forEach(consumer);
+ }
+
+ public MethodState get(ProgramMethod method) {
+ return methodStates.getOrDefault(getKey(method), MethodState.bottom());
+ }
+
+ public boolean isEmpty() {
+ return methodStates.isEmpty();
+ }
+
+ public MethodState remove(ProgramMethod method) {
+ MethodState removed = methodStates.remove(getKey(method));
+ return removed != null ? removed : MethodState.bottom();
+ }
+
+ public void set(ProgramMethod method, MethodState methodState) {
+ methodStates.put(getKey(method), methodState);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollectionByReference.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollectionByReference.java
new file mode 100644
index 0000000..ea48bfb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollectionByReference.java
@@ -0,0 +1,31 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.codescanner;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.ProgramMethod;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class MethodStateCollectionByReference extends MethodStateCollection<DexMethod> {
+
+ private MethodStateCollectionByReference(Map<DexMethod, MethodState> methodStates) {
+ super(methodStates);
+ }
+
+ public static MethodStateCollectionByReference create() {
+ return new MethodStateCollectionByReference(new IdentityHashMap<>());
+ }
+
+ public static MethodStateCollectionByReference createConcurrent() {
+ return new MethodStateCollectionByReference(new ConcurrentHashMap<>());
+ }
+
+ @Override
+ DexMethod getKey(ProgramMethod method) {
+ return method.getReference();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollectionBySignature.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollectionBySignature.java
new file mode 100644
index 0000000..2113be0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollectionBySignature.java
@@ -0,0 +1,31 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.codescanner;
+
+import com.android.tools.r8.graph.DexMethodSignature;
+import com.android.tools.r8.graph.ProgramMethod;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class MethodStateCollectionBySignature extends MethodStateCollection<DexMethodSignature> {
+
+ private MethodStateCollectionBySignature(Map<DexMethodSignature, MethodState> methodStates) {
+ super(methodStates);
+ }
+
+ public static MethodStateCollectionBySignature create() {
+ return new MethodStateCollectionBySignature(new HashMap<>());
+ }
+
+ public static MethodStateCollectionBySignature createConcurrent() {
+ return new MethodStateCollectionBySignature(new ConcurrentHashMap<>());
+ }
+
+ @Override
+ DexMethodSignature getKey(ProgramMethod method) {
+ return method.getMethodSignature();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ParameterState.java
new file mode 100644
index 0000000..2c9a205
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ParameterState.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.codescanner;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Action;
+
+public abstract class ParameterState {
+
+ public static UnknownParameterState unknown() {
+ return UnknownParameterState.get();
+ }
+
+ public abstract AbstractValue getAbstractValue();
+
+ public boolean isConcrete() {
+ return false;
+ }
+
+ public ConcreteParameterState asConcrete() {
+ return null;
+ }
+
+ public boolean isUnknown() {
+ return false;
+ }
+
+ public final ParameterState mutableJoin(
+ AppView<AppInfoWithLiveness> appView, ParameterState parameterState) {
+ return mutableJoin(appView, parameterState, Action.empty());
+ }
+
+ public abstract ParameterState mutableJoin(
+ AppView<AppInfoWithLiveness> appView, ParameterState parameterState, Action onChangedAction);
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownMethodState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownMethodState.java
new file mode 100644
index 0000000..1cfe728
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownMethodState.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.codescanner;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.function.Supplier;
+
+// Use this when the nothing is known.
+public class UnknownMethodState extends MethodStateBase
+ implements ConcreteMonomorphicMethodStateOrUnknown {
+
+ private static final UnknownMethodState INSTANCE = new UnknownMethodState();
+
+ private UnknownMethodState() {}
+
+ public static UnknownMethodState get() {
+ return INSTANCE;
+ }
+
+ @Override
+ public boolean isUnknown() {
+ return true;
+ }
+
+ @Override
+ public MethodState mutableJoin(AppView<AppInfoWithLiveness> appView, MethodState methodState) {
+ return this;
+ }
+
+ @Override
+ public MethodState mutableJoin(
+ AppView<AppInfoWithLiveness> appView, Supplier<MethodState> methodStateSupplier) {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownParameterState.java
new file mode 100644
index 0000000..bfc835d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownParameterState.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.codescanner;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Action;
+
+public class UnknownParameterState extends ParameterState {
+
+ private static final UnknownParameterState INSTANCE = new UnknownParameterState();
+
+ private UnknownParameterState() {}
+
+ public static UnknownParameterState get() {
+ return INSTANCE;
+ }
+
+ @Override
+ public AbstractValue getAbstractValue() {
+ return AbstractValue.unknown();
+ }
+
+ @Override
+ public boolean isUnknown() {
+ return true;
+ }
+
+ @Override
+ public ParameterState mutableJoin(
+ AppView<AppInfoWithLiveness> appView, ParameterState parameterState, Action onChangedAction) {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InParameterFlowPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InParameterFlowPropagator.java
new file mode 100644
index 0000000..cba390e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InParameterFlowPropagator.java
@@ -0,0 +1,302 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.propagation;
+
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMethodState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameter;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ParameterState;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.Action;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.google.common.collect.Sets;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
+import java.util.ArrayDeque;
+import java.util.Deque;
+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;
+import java.util.function.Consumer;
+
+public class InParameterFlowPropagator {
+
+ final AppView<AppInfoWithLiveness> appView;
+ final MethodStateCollectionByReference methodStates;
+
+ public InParameterFlowPropagator(
+ AppView<AppInfoWithLiveness> appView, MethodStateCollectionByReference methodStates) {
+ this.appView = appView;
+ this.methodStates = methodStates;
+ }
+
+ public void run(ExecutorService executorService) throws ExecutionException {
+ // Build a graph with an edge from parameter p -> parameter p' if all argument information for p
+ // must be included in the argument information for p'.
+ FlowGraph flowGraph = new FlowGraph(appView.appInfo().classes());
+
+ // Build a worklist containing all the parameter nodes.
+ Deque<ParameterNode> worklist = new ArrayDeque<>();
+ flowGraph.forEachNode(worklist::add);
+
+ // Repeatedly propagate argument information through edges in the flow graph until there are no
+ // more changes.
+ // TODO(b/190154391): Consider parallelizing the flow propagation. There are a few scenarios
+ // that need to be covered, such as (i) two threads could race to update the same parameter
+ // state, (ii) a thread may try to propagate a parameter state to its successors while
+ // another thread is trying to update the state of the parameter itself.
+ // TODO(b/190154391): Consider a path p1 -> p2 -> p3 in the graph. If we process p2 first, then
+ // p3, and then p1, then the processing of p1 could cause p2 to change, which means that we
+ // need to reprocess p2 and then p3. If we always process leaves in the graph first, we would
+ // process p1, then p2, then p3, and then be done.
+ // TODO(b/190154391): Prune the graph on-the-fly. If the argument information for a parameter
+ // becomes unknown, we could consider clearing its predecessors since none of the predecessors
+ // could contribute any information even if they change.
+ while (!worklist.isEmpty()) {
+ ParameterNode parameterNode = worklist.removeLast();
+ parameterNode.unsetPending();
+ propagate(
+ parameterNode,
+ affectedNode -> {
+ // No need to enqueue the affected node if it is already in the worklist or if it does
+ // not have any successors (i.e., the successor is a leaf).
+ if (!affectedNode.isPending() && affectedNode.hasSuccessors()) {
+ worklist.add(affectedNode);
+ affectedNode.setPending();
+ }
+ });
+ }
+
+ // The algorithm only changes the parameter states of each monomorphic method state. In case any
+ // of these method states have effectively become unknown, we replace them by the canonicalized
+ // unknown method state.
+ postProcessMethodStates(executorService);
+ }
+
+ private void propagate(
+ ParameterNode parameterNode, Consumer<ParameterNode> affectedNodeConsumer) {
+ ParameterState parameterState = parameterNode.getState();
+ for (ParameterNode successorNode : parameterNode.getSuccessors()) {
+ successorNode.addState(
+ appView, parameterState, () -> affectedNodeConsumer.accept(successorNode));
+ }
+ }
+
+ private void postProcessMethodStates(ExecutorService executorService) throws ExecutionException {
+ ThreadUtils.processItems(
+ appView.appInfo().classes(), this::postProcessMethodStates, executorService);
+ }
+
+ private void postProcessMethodStates(DexProgramClass clazz) {
+ clazz.forEachProgramMethod(this::postProcessMethodState);
+ }
+
+ private void postProcessMethodState(ProgramMethod method) {
+ ConcreteMethodState methodState = methodStates.get(method).asConcrete();
+ if (methodState == null) {
+ return;
+ }
+ assert methodState.isMonomorphic();
+ for (ParameterState parameterState : methodState.asMonomorphic().getParameterStates()) {
+ if (!parameterState.isUnknown()) {
+ return;
+ }
+ }
+ methodStates.set(method, MethodState.unknown());
+ }
+
+ private class FlowGraph {
+
+ private final Map<DexMethod, Int2ReferenceMap<ParameterNode>> nodes = new IdentityHashMap<>();
+
+ public FlowGraph(Iterable<DexProgramClass> classes) {
+ classes.forEach(this::add);
+ }
+
+ void forEachNode(Consumer<? super ParameterNode> consumer) {
+ nodes.values().forEach(nodesForMethod -> nodesForMethod.values().forEach(consumer));
+ }
+
+ private void add(DexProgramClass clazz) {
+ clazz.forEachProgramMethod(this::add);
+ }
+
+ private void add(ProgramMethod method) {
+ MethodState methodState = methodStates.get(method);
+
+ // No need to create nodes for parameters with no in-flow or no useful information.
+ if (methodState.isBottom() || methodState.isUnknown()) {
+ return;
+ }
+
+ // Add nodes for the parameters for which we have non-trivial information.
+ ConcreteMonomorphicMethodState monomorphicMethodState =
+ methodState.asConcrete().asMonomorphic();
+ List<ParameterState> parameterStates = monomorphicMethodState.getParameterStates();
+ for (int parameterIndex = 0; parameterIndex < parameterStates.size(); parameterIndex++) {
+ ParameterState parameterState = parameterStates.get(parameterIndex);
+ add(method, parameterIndex, monomorphicMethodState, parameterState);
+ }
+ }
+
+ private void add(
+ ProgramMethod method,
+ int parameterIndex,
+ ConcreteMonomorphicMethodState methodState,
+ ParameterState parameterState) {
+ // No need to create nodes for parameters we don't know anything about.
+ if (parameterState.isUnknown()) {
+ return;
+ }
+
+ ConcreteParameterState concreteParameterState = parameterState.asConcrete();
+
+ // No need to create a node for a parameter that doesn't depend on any other parameters
+ // (unless some other parameter depends on this parameter).
+ if (!concreteParameterState.hasInParameters()) {
+ return;
+ }
+
+ ParameterNode node =
+ getOrCreateParameterNode(method.getReference(), parameterIndex, methodState);
+ for (MethodParameter inParameter : concreteParameterState.getInParameters()) {
+ MethodState enclosingMethodState = getEnclosingMethodStateForParameter(inParameter);
+ if (enclosingMethodState.isBottom()) {
+ // The current method is called from a dead method; no need to propagate any information
+ // from the dead call site.
+ continue;
+ }
+
+ if (enclosingMethodState.isUnknown()) {
+ // The parameter depends on another parameter for which we don't know anything.
+ node.clearPredecessors();
+ node.setState(ParameterState.unknown());
+ break;
+ }
+
+ assert enclosingMethodState.isConcrete();
+ assert enclosingMethodState.asConcrete().isMonomorphic();
+
+ ParameterNode predecessor =
+ getOrCreateParameterNode(
+ inParameter.getMethod(),
+ inParameter.getIndex(),
+ enclosingMethodState.asConcrete().asMonomorphic());
+ node.addPredecessor(predecessor);
+ }
+ concreteParameterState.clearInParameters();
+ }
+
+ private ParameterNode getOrCreateParameterNode(
+ DexMethod key, int parameterIndex, ConcreteMonomorphicMethodState methodState) {
+ Int2ReferenceMap<ParameterNode> parameterNodesForMethod =
+ nodes.computeIfAbsent(key, ignoreKey(Int2ReferenceOpenHashMap::new));
+ return parameterNodesForMethod.compute(
+ parameterIndex,
+ (ignore, parameterNode) ->
+ parameterNode != null
+ ? parameterNode
+ : new ParameterNode(methodState, parameterIndex));
+ }
+
+ private MethodState getEnclosingMethodStateForParameter(MethodParameter methodParameter) {
+ DexMethod methodReference = methodParameter.getMethod();
+ ProgramMethod method =
+ methodReference.lookupOnProgramClass(
+ asProgramClassOrNull(
+ appView.definitionFor(methodParameter.getMethod().getHolderType())));
+ if (method == null) {
+ // Conservatively return unknown if for some reason we can't find the method.
+ assert false;
+ return MethodState.unknown();
+ }
+ return methodStates.get(method);
+ }
+ }
+
+ static class ParameterNode {
+
+ private final ConcreteMonomorphicMethodState methodState;
+ private final int parameterIndex;
+
+ private final Set<ParameterNode> predecessors = Sets.newIdentityHashSet();
+ private final Set<ParameterNode> successors = Sets.newIdentityHashSet();
+
+ private boolean pending = true;
+
+ ParameterNode(ConcreteMonomorphicMethodState methodState, int parameterIndex) {
+ this.methodState = methodState;
+ this.parameterIndex = parameterIndex;
+ }
+
+ void addPredecessor(ParameterNode predecessor) {
+ predecessor.successors.add(this);
+ predecessors.add(predecessor);
+ }
+
+ void clearPredecessors() {
+ for (ParameterNode predecessor : predecessors) {
+ predecessor.successors.remove(this);
+ }
+ predecessors.clear();
+ }
+
+ ParameterState getState() {
+ return methodState.getParameterState(parameterIndex);
+ }
+
+ Set<ParameterNode> getSuccessors() {
+ return successors;
+ }
+
+ boolean hasSuccessors() {
+ return !successors.isEmpty();
+ }
+
+ boolean isPending() {
+ return pending;
+ }
+
+ void addState(
+ AppView<AppInfoWithLiveness> appView,
+ ParameterState parameterStateToAdd,
+ Action onChangedAction) {
+ ParameterState oldParameterState = getState();
+ ParameterState newParameterState =
+ oldParameterState.mutableJoin(appView, parameterStateToAdd, onChangedAction);
+ if (newParameterState != oldParameterState) {
+ setState(newParameterState);
+ onChangedAction.execute();
+ }
+ }
+
+ void setPending() {
+ assert !isPending();
+ pending = true;
+ }
+
+ void setState(ParameterState parameterState) {
+ methodState.setParameterState(parameterIndex, parameterState);
+ }
+
+ void unsetPending() {
+ assert pending;
+ pending = false;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java
new file mode 100644
index 0000000..99a0bac
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java
@@ -0,0 +1,161 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.propagation;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionBySignature;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+
+/**
+ * Propagates the argument information for each interface method I.m() to the possible dispatch
+ * targets that are declared on (non-interface) classes that are not a subtype of I.
+ *
+ * <p>Example: The argument information stored for I.m() is propagated to the method A.m().
+ *
+ * <pre>
+ * interface I { void m(int x); }
+ * class A { public void m(int x) { ... } }
+ * class B extends A implements I {}
+ * </pre>
+ */
+public class InterfaceMethodArgumentPropagator extends MethodArgumentPropagator {
+
+ // Contains the argument information for each interface method (including inherited interface
+ // methods) on the seen but not finished interfaces.
+ final Map<DexProgramClass, MethodStateCollectionBySignature> methodStatesToPropagate =
+ new IdentityHashMap<>();
+
+ public InterfaceMethodArgumentPropagator(
+ AppView<AppInfoWithLiveness> appView,
+ ImmediateProgramSubtypingInfo immediateSubtypingInfo,
+ MethodStateCollectionByReference methodStates) {
+ super(appView, immediateSubtypingInfo, methodStates);
+ }
+
+ @Override
+ public void run(Collection<DexProgramClass> stronglyConnectedComponent) {
+ super.run(stronglyConnectedComponent);
+ assert verifyAllInterfacesFinished(stronglyConnectedComponent);
+ }
+
+ @Override
+ public void forEachSubClass(DexProgramClass clazz, Consumer<DexProgramClass> consumer) {
+ for (DexProgramClass subclass : immediateSubtypingInfo.getSubclasses(clazz)) {
+ if (subclass.isInterface()) {
+ consumer.accept(subclass);
+ }
+ }
+ }
+
+ @Override
+ public boolean isRoot(DexProgramClass clazz) {
+ return clazz.isInterface() && super.isRoot(clazz);
+ }
+
+ @Override
+ public void visit(DexProgramClass clazz) {
+ assert !methodStatesToPropagate.containsKey(clazz);
+ MethodStateCollectionBySignature interfaceState = computeInterfaceState(clazz);
+ propagateInterfaceStateToClassHierarchy(clazz, interfaceState);
+ }
+
+ @Override
+ public void prune(DexProgramClass clazz) {
+ methodStatesToPropagate.remove(clazz);
+ }
+
+ private MethodStateCollectionBySignature computeInterfaceState(
+ DexProgramClass interfaceDefinition) {
+ // Join the state for all parent interfaces into a fresh state created for this interface.
+ MethodStateCollectionBySignature interfaceState = MethodStateCollectionBySignature.create();
+ immediateSubtypingInfo.forEachImmediateSuperClassMatching(
+ interfaceDefinition,
+ (supertype, superclass) -> superclass != null && superclass.isProgramClass(),
+ (supertype, superclass) -> {
+ MethodStateCollectionBySignature implementedInterfaceState =
+ methodStatesToPropagate.get(superclass.asProgramClass());
+ assert implementedInterfaceState != null;
+ interfaceState.addMethodStates(appView, implementedInterfaceState);
+ });
+
+ // Add any argument information for virtual methods on the current interface to the state.
+ interfaceDefinition.forEachProgramVirtualMethod(
+ method -> {
+ MethodState methodState = methodStates.get(method);
+ if (methodState.isBottom()) {
+ return;
+ }
+
+ // TODO(b/190154391): We should always have an unknown or polymorphic state, but it would
+ // be better to use a monomorphic state when the interface method is a default method
+ // with no overrides (CF backend only). In this case, there is no need to add methodState
+ // to interfaceState.
+ assert methodState.isUnknown() || methodState.asConcrete().isPolymorphic();
+ interfaceState.addMethodState(appView, method, methodState);
+ });
+
+ methodStatesToPropagate.put(interfaceDefinition, interfaceState);
+ return interfaceState;
+ }
+
+ private void propagateInterfaceStateToClassHierarchy(
+ DexProgramClass interfaceDefinition, MethodStateCollectionBySignature interfaceState) {
+ // Propagate the argument information for the interface's non-private virtual methods to the
+ // the possible dispatch targets declared on classes that are not a subtype of the interface.
+ immediateSubtypingInfo.forEachImmediateSubClassMatching(
+ interfaceDefinition,
+ subclass -> !subclass.isInterface(),
+ subclass ->
+ interfaceState.forEach(
+ (interfaceMethod, interfaceMethodState) -> {
+ // TODO(b/190154391): Change resolution to take a signature.
+ DexMethod interfaceMethodToResolve =
+ appView
+ .dexItemFactory()
+ .createMethod(
+ subclass.getType(),
+ interfaceMethod.getProto(),
+ interfaceMethod.getName());
+ SingleResolutionResult resolutionResult =
+ appView
+ .appInfo()
+ .resolveMethodOnClass(interfaceMethodToResolve, subclass)
+ .asSingleResolution();
+ if (resolutionResult == null) {
+ assert false;
+ return;
+ }
+
+ ProgramMethod resolvedMethod = resolutionResult.getResolvedProgramMethod();
+ if (resolvedMethod == null
+ || resolvedMethod.getHolder().isInterface()
+ || resolvedMethod.getHolder() == subclass) {
+ return;
+ }
+
+ methodStates.addMethodState(appView, resolvedMethod, interfaceMethodState);
+ }));
+ }
+
+ private boolean verifyAllInterfacesFinished(
+ Collection<DexProgramClass> stronglyConnectedComponent) {
+ assert stronglyConnectedComponent.stream()
+ .filter(DexClass::isInterface)
+ .allMatch(this::isClassFinished);
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/MethodArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/MethodArgumentPropagator.java
new file mode 100644
index 0000000..4e6723f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/MethodArgumentPropagator.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.propagation;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
+import com.android.tools.r8.optimize.argumentpropagation.utils.DepthFirstTopDownClassHierarchyTraversal;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
+public abstract class MethodArgumentPropagator extends DepthFirstTopDownClassHierarchyTraversal {
+
+ final MethodStateCollectionByReference methodStates;
+
+ public MethodArgumentPropagator(
+ AppView<AppInfoWithLiveness> appView,
+ ImmediateProgramSubtypingInfo immediateSubtypingInfo,
+ MethodStateCollectionByReference methodStates) {
+ super(appView, immediateSubtypingInfo);
+ this.methodStates = methodStates;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java
new file mode 100644
index 0000000..14c58f8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/VirtualDispatchMethodArgumentPropagator.java
@@ -0,0 +1,254 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.propagation;
+
+import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
+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.graph.ImmediateProgramSubtypingInfo;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMethodState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcretePolymorphicMethodState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionBySignature;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+public class VirtualDispatchMethodArgumentPropagator extends MethodArgumentPropagator {
+
+ class PropagationState {
+
+ // Argument information for virtual methods that must be propagated to all overrides (i.e., this
+ // information does not have a lower bound).
+ final MethodStateCollectionBySignature active = MethodStateCollectionBySignature.create();
+
+ // Argument information for virtual methods that must be propagated to all overrides that are
+ // above the given lower bound.
+ final Map<DexType, MethodStateCollectionBySignature> activeUntilLowerBound =
+ new IdentityHashMap<>();
+
+ // Argument information for virtual methods that is currently inactive, but should be propagated
+ // to all overrides below a given upper bound.
+ final Map<DynamicType, MethodStateCollectionBySignature> inactiveUntilUpperBound =
+ new HashMap<>();
+
+ PropagationState(DexProgramClass clazz) {
+ // Join the argument information from each of the super types.
+ immediateSubtypingInfo.forEachImmediateSuperClassMatching(
+ clazz,
+ (supertype, superclass) -> superclass != null && superclass.isProgramClass(),
+ (supertype, superclass) -> addParentState(clazz, superclass.asProgramClass()));
+ }
+
+ // TODO(b/190154391): This currently copies the state of the superclass into its immediate
+ // given subclass. Instead of copying the state, consider linking the states. This would reduce
+ // memory usage, but would require visiting all transitive (program) super classes for each
+ // subclass.
+ private void addParentState(DexProgramClass clazz, DexProgramClass superclass) {
+ ClassTypeElement classType =
+ TypeElement.fromDexType(clazz.getType(), maybeNull(), appView).asClassType();
+
+ PropagationState parentState = propagationStates.get(superclass.asProgramClass());
+ assert parentState != null;
+
+ // Add the argument information that must be propagated to all method overrides.
+ active.addMethodStates(appView, parentState.active);
+
+ // Add the argument information that is active until a given lower bound.
+ parentState.activeUntilLowerBound.forEach(
+ (lowerBound, activeMethodState) -> {
+ if (lowerBound != superclass.getType()) {
+ // TODO(b/190154391): Verify that the lower bound is a subtype of the current.
+ // Otherwise we carry this information to all subtypes although there is no need to.
+ activeUntilLowerBound
+ .computeIfAbsent(lowerBound, ignoreKey(MethodStateCollectionBySignature::create))
+ .addMethodStates(appView, activeMethodState);
+ } else {
+ // No longer active.
+ }
+ });
+
+ // Add the argument information that is inactive until a given upper bound.
+ parentState.inactiveUntilUpperBound.forEach(
+ (bounds, inactiveMethodState) -> {
+ ClassTypeElement upperBound = bounds.getDynamicUpperBoundType().asClassType();
+ if (upperBound.equalUpToNullability(classType)) {
+ // The upper bound is the current class, thus this inactive information now becomes
+ // active.
+ if (bounds.hasDynamicLowerBoundType()) {
+ activeUntilLowerBound
+ .computeIfAbsent(
+ bounds.getDynamicLowerBoundType().getClassType(),
+ ignoreKey(MethodStateCollectionBySignature::create))
+ .addMethodStates(appView, inactiveMethodState);
+ } else {
+ active.addMethodStates(appView, inactiveMethodState);
+ }
+ } else {
+ // Still inactive.
+ // TODO(b/190154391): Only carry this information downwards if the upper bound is a
+ // subtype of this class. Otherwise we carry this information to all subtypes,
+ // although clearly the information will never become active.
+ inactiveUntilUpperBound
+ .computeIfAbsent(bounds, ignoreKey(MethodStateCollectionBySignature::create))
+ .addMethodStates(appView, inactiveMethodState);
+ }
+ });
+ }
+
+ private MethodState computeMethodStateForPolymorhicMethod(ProgramMethod method) {
+ assert method.getDefinition().isNonPrivateVirtualMethod();
+ MethodState methodState = active.get(method);
+ for (MethodStateCollectionBySignature methodStates : activeUntilLowerBound.values()) {
+ methodState = methodState.mutableJoin(appView, methodStates.get(method));
+ }
+ return methodState;
+ }
+ }
+
+ // For each class, stores the argument information for each virtual method on this class and all
+ // direct and indirect super classes.
+ //
+ // This data structure is populated during a top-down traversal over the class hierarchy, such
+ // that entries in the map can be removed when the top-down traversal has visited all subtypes of
+ // a given node.
+ final Map<DexProgramClass, PropagationState> propagationStates = new IdentityHashMap<>();
+
+ public VirtualDispatchMethodArgumentPropagator(
+ AppView<AppInfoWithLiveness> appView,
+ ImmediateProgramSubtypingInfo immediateSubtypingInfo,
+ MethodStateCollectionByReference methodStates) {
+ super(appView, immediateSubtypingInfo, methodStates);
+ }
+
+ @Override
+ public void run(Collection<DexProgramClass> stronglyConnectedComponent) {
+ super.run(stronglyConnectedComponent);
+ assert verifyAllClassesFinished(stronglyConnectedComponent);
+ assert verifyStatePruned();
+ }
+
+ @Override
+ public void visit(DexProgramClass clazz) {
+ assert !propagationStates.containsKey(clazz);
+ PropagationState propagationState = computePropagationState(clazz);
+ computeFinalMethodStates(clazz, propagationState);
+ }
+
+ private PropagationState computePropagationState(DexProgramClass clazz) {
+ ClassTypeElement classType =
+ TypeElement.fromDexType(clazz.getType(), maybeNull(), appView).asClassType();
+ PropagationState propagationState = new PropagationState(clazz);
+
+ // Join the argument information from the methods of the current class.
+ clazz.forEachProgramVirtualMethod(
+ method -> {
+ MethodState methodState = methodStates.get(method);
+ if (methodState.isBottom()) {
+ return;
+ }
+
+ // TODO(b/190154391): Add an unknown polymorphic method state, such that we can
+ // distinguish monomorphic unknown method states from polymorphic unknown method states.
+ // We only need to propagate polymorphic unknown method states here.
+ if (methodState.isUnknown()) {
+ propagationState.active.addMethodState(appView, method, methodState);
+ return;
+ }
+
+ ConcreteMethodState concreteMethodState = methodState.asConcrete();
+ if (concreteMethodState.isMonomorphic()) {
+ // No need to propagate information for methods that do not override other methods and
+ // are not themselves overridden.
+ return;
+ }
+
+ ConcretePolymorphicMethodState polymorphicMethodState =
+ concreteMethodState.asPolymorphic();
+ polymorphicMethodState.forEach(
+ (bounds, methodStateForBounds) -> {
+ if (bounds.isUnknown()) {
+ propagationState.active.addMethodState(appView, method, methodStateForBounds);
+ } else {
+ // TODO(b/190154391): Verify that the bounds are not trivial according to the
+ // static receiver type.
+ ClassTypeElement upperBound = bounds.getDynamicUpperBoundType().asClassType();
+ if (upperBound.equalUpToNullability(classType)) {
+ if (bounds.hasDynamicLowerBoundType()) {
+ // TODO(b/190154391): Verify that the lower bound is a subtype of the current
+ // class.
+ propagationState
+ .activeUntilLowerBound
+ .computeIfAbsent(
+ bounds.getDynamicLowerBoundType().getClassType(),
+ ignoreKey(MethodStateCollectionBySignature::create))
+ .addMethodState(appView, method, methodStateForBounds);
+ } else {
+ propagationState.active.addMethodState(appView, method, methodStateForBounds);
+ }
+ } else {
+ assert !classType.lessThanOrEqualUpToNullability(upperBound, appView);
+ propagationState
+ .inactiveUntilUpperBound
+ .computeIfAbsent(
+ bounds, ignoreKey(MethodStateCollectionBySignature::create))
+ .addMethodState(appView, method, methodStateForBounds);
+ }
+ }
+ });
+ });
+
+ propagationStates.put(clazz, propagationState);
+ return propagationState;
+ }
+
+ private void computeFinalMethodStates(DexProgramClass clazz, PropagationState propagationState) {
+ clazz.forEachProgramMethod(method -> computeFinalMethodState(method, propagationState));
+ }
+
+ private void computeFinalMethodState(ProgramMethod method, PropagationState propagationState) {
+ if (!method.getDefinition().hasCode()) {
+ methodStates.remove(method);
+ return;
+ }
+
+ MethodState methodState = methodStates.get(method);
+
+ // If this is a polymorphic method, we need to compute the method state to account for dynamic
+ // dispatch.
+ if (methodState.isConcrete() && methodState.asConcrete().isPolymorphic()) {
+ methodState = propagationState.computeMethodStateForPolymorhicMethod(method);
+ assert !methodState.isConcrete() || methodState.asConcrete().isMonomorphic();
+ methodStates.set(method, methodState);
+ }
+ }
+
+ @Override
+ public void prune(DexProgramClass clazz) {
+ propagationStates.remove(clazz);
+ }
+
+ private boolean verifyAllClassesFinished(Collection<DexProgramClass> stronglyConnectedComponent) {
+ for (DexProgramClass clazz : stronglyConnectedComponent) {
+ assert isClassFinished(clazz);
+ }
+ return true;
+ }
+
+ private boolean verifyStatePruned() {
+ assert propagationStates.isEmpty();
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/DepthFirstTopDownClassHierarchyTraversal.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/DepthFirstTopDownClassHierarchyTraversal.java
new file mode 100644
index 0000000..379efaf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/DepthFirstTopDownClassHierarchyTraversal.java
@@ -0,0 +1,190 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation.utils;
+
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+
+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.graph.ImmediateProgramSubtypingInfo;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+
+public abstract class DepthFirstTopDownClassHierarchyTraversal {
+
+ // The state of a given class in the top-down traversal.
+ private enum TraversalState {
+ // Represents that a given class and all of its direct and indirect supertypes have been
+ // visited by the top-down traversal, but all of the direct and indirect subtypes are still
+ // not visited.
+ SEEN,
+ // Represents that a given class and all of its direct and indirect subtypes have been visited.
+ // Such nodes will never be seen again in the top-down traversal, and any state stored for
+ // such nodes can be pruned.
+ FINISHED
+ }
+
+ protected final AppView<AppInfoWithLiveness> appView;
+ protected final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
+
+ // Contains the traversal state for each class. If a given class is not in the map the class is
+ // not yet seen.
+ private final Map<DexProgramClass, TraversalState> states = new IdentityHashMap<>();
+
+ // The class hierarchy is not a tree, thus for completeness we need to process all parent
+ // interfaces for a given class or interface before continuing the top-down traversal. When the
+ // top-down traversal for a given root returns, this means that there may be interfaces that are
+ // seen but not finished. These interfaces are added to this collection such that we can
+ // prioritize them over classes or interfaces that are yet not seen. This leads to more efficient
+ // state pruning, since the state for these interfaces can be pruned when they transition to being
+ // finished.
+ //
+ // See also prioritizeNewlySeenButNotFinishedRoots().
+ private final List<DexProgramClass> newlySeenButNotFinishedRoots = new ArrayList<>();
+
+ public DepthFirstTopDownClassHierarchyTraversal(
+ AppView<AppInfoWithLiveness> appView, ImmediateProgramSubtypingInfo immediateSubtypingInfo) {
+ this.appView = appView;
+ this.immediateSubtypingInfo = immediateSubtypingInfo;
+ }
+
+ public abstract void visit(DexProgramClass clazz);
+
+ public abstract void prune(DexProgramClass clazz);
+
+ public void run(Collection<DexProgramClass> stronglyConnectedComponent) {
+ // Perform a top-down traversal from each root in the strongly connected component.
+ Deque<DexProgramClass> roots = computeRoots(stronglyConnectedComponent);
+ while (!roots.isEmpty()) {
+ DexProgramClass root = roots.removeLast();
+ traverse(root);
+ prioritizeNewlySeenButNotFinishedRoots(roots);
+ }
+ }
+
+ private Deque<DexProgramClass> computeRoots(
+ Collection<DexProgramClass> stronglyConnectedComponent) {
+ Deque<DexProgramClass> roots = new ArrayDeque<>();
+ for (DexProgramClass clazz : stronglyConnectedComponent) {
+ if (isRoot(clazz)) {
+ roots.add(clazz);
+ }
+ }
+ return roots;
+ }
+
+ public boolean isRoot(DexProgramClass clazz) {
+ DexProgramClass superclass = asProgramClassOrNull(appView.definitionFor(clazz.getSuperType()));
+ if (superclass != null) {
+ return false;
+ }
+ for (DexType implementedType : clazz.getInterfaces()) {
+ DexProgramClass implementedClass =
+ asProgramClassOrNull(appView.definitionFor(implementedType));
+ if (implementedClass != null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void prioritizeNewlySeenButNotFinishedRoots(Deque<DexProgramClass> roots) {
+ assert newlySeenButNotFinishedRoots.stream()
+ .allMatch(
+ newlySeenButNotFinishedRoot -> {
+ assert newlySeenButNotFinishedRoot.isInterface();
+ assert isRoot(newlySeenButNotFinishedRoot);
+ assert isClassSeenButNotFinished(newlySeenButNotFinishedRoot);
+ return true;
+ });
+ // Prioritize this interface over other not yet seen interfaces. This leads to more efficient
+ // state pruning.
+ roots.addAll(newlySeenButNotFinishedRoots);
+ newlySeenButNotFinishedRoots.clear();
+ }
+
+ private void traverse(DexProgramClass clazz) {
+ // Check it the class and all of its subtypes are already processed.
+ if (isClassFinished(clazz)) {
+ return;
+ }
+
+ // Before continuing the top-down traversal, ensure that all super interfaces are processed,
+ // but without visiting the entire subtree of each super interface.
+ if (!isClassSeenButNotFinished(clazz)) {
+ processImplementedInterfaces(clazz);
+ processClass(clazz);
+ }
+
+ processSubclasses(clazz);
+ markFinished(clazz);
+ }
+
+ private void processImplementedInterfaces(DexProgramClass interfaceDefinition) {
+ assert !isClassSeenButNotFinished(interfaceDefinition);
+ assert !isClassFinished(interfaceDefinition);
+ for (DexType implementedType : interfaceDefinition.getInterfaces()) {
+ DexProgramClass implementedDefinition =
+ asProgramClassOrNull(appView.definitionFor(implementedType));
+ if (implementedDefinition == null || isClassSeenButNotFinished(implementedDefinition)) {
+ continue;
+ }
+ assert isClassUnseen(implementedDefinition);
+ processImplementedInterfaces(implementedDefinition);
+ processClass(implementedDefinition);
+
+ // If this is a root, then record that this root is seen but not finished.
+ if (isRoot(implementedDefinition)) {
+ newlySeenButNotFinishedRoots.add(implementedDefinition);
+ }
+ }
+ }
+
+ private void processSubclasses(DexProgramClass clazz) {
+ forEachSubClass(clazz, this::traverse);
+ }
+
+ public void forEachSubClass(DexProgramClass clazz, Consumer<DexProgramClass> consumer) {
+ immediateSubtypingInfo.getSubclasses(clazz).forEach(consumer);
+ }
+
+ private void processClass(DexProgramClass interfaceDefinition) {
+ assert !isClassSeenButNotFinished(interfaceDefinition);
+ assert !isClassFinished(interfaceDefinition);
+ visit(interfaceDefinition);
+ markSeenButNotFinished(interfaceDefinition);
+ }
+
+ protected boolean isClassUnseen(DexProgramClass clazz) {
+ return !states.containsKey(clazz);
+ }
+
+ protected boolean isClassSeenButNotFinished(DexProgramClass clazz) {
+ return states.get(clazz) == TraversalState.SEEN;
+ }
+
+ protected boolean isClassFinished(DexProgramClass clazz) {
+ return states.get(clazz) == TraversalState.FINISHED;
+ }
+
+ private void markSeenButNotFinished(DexProgramClass clazz) {
+ assert isClassUnseen(clazz);
+ states.put(clazz, TraversalState.SEEN);
+ }
+
+ private void markFinished(DexProgramClass clazz) {
+ assert isClassSeenButNotFinished(clazz);
+ states.put(clazz, TraversalState.FINISHED);
+ prune(clazz);
+ }
+}
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 69c59b5..5550d52 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
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.BottomUpClassHierarchyTraversal;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMember;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -22,8 +23,8 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodAccessInfoCollection;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
@@ -34,6 +35,7 @@
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -120,7 +122,7 @@
private void processClass(DexProgramClass clazz, SubtypingInfo subtypingInfo) {
Set<DexType> subtypes = subtypingInfo.allImmediateSubtypes(clazz.type);
- Set<DexProgramClass> subclasses = new TreeSet<>((x, y) -> x.type.compareTo(y.type));
+ Set<DexProgramClass> subclasses = new TreeSet<>(Comparator.comparing(DexClass::getType));
for (DexType subtype : subtypes) {
DexProgramClass subclass = asProgramClassOrNull(appView.definitionFor(subtype));
if (subclass == null) {
@@ -225,7 +227,7 @@
appView.dexItemFactory().createMethod(clazz.type, invokedMethod.proto, invokedMethod.name);
// The targeted method must be present on the new holder class for this to be feasible.
- ResolutionResult resolutionResult =
+ MethodResolutionResult resolutionResult =
appView.appInfo().resolveMethodOnClass(methodToInvoke, clazz);
if (!resolutionResult.isSingleResolution()) {
return;
diff --git a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
index e193f4c..6621857 100644
--- a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
+++ b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
@@ -28,6 +28,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.BiMap;
@@ -319,12 +320,14 @@
private final AppView<?> appView;
private final DexItemFactory dexItemFactory;
+ private final InternalOptions options;
private final ProguardConfiguration proguardConfiguration;
private final MinificationPackageNamingStrategy packageMinificationStrategy;
public DefaultRepackagingConfiguration(AppView<?> appView) {
this.appView = appView;
this.dexItemFactory = appView.dexItemFactory();
+ this.options = appView.options();
this.proguardConfiguration = appView.options().getProguardConfiguration();
this.packageMinificationStrategy = new MinificationPackageNamingStrategy(appView);
}
@@ -383,12 +386,12 @@
// item, in which case we cannot move it because there may be a reflective access to it.
for (DexProgramClass clazz : pkg.classesInPackage()) {
if (clazz.getAccessFlags().isPackagePrivateOrProtected()
- && appView.getKeepInfo().getClassInfo(clazz).isPinned()) {
+ && appView.getKeepInfo().getClassInfo(clazz).isPinned(options)) {
return true;
}
for (DexEncodedMember<?, ?> member : clazz.members()) {
if (member.getAccessFlags().isPackagePrivateOrProtected()
- && appView.getKeepInfo().getMemberInfo(member, clazz).isPinned()) {
+ && appView.getKeepInfo().getMemberInfo(member, clazz).isPinned(options)) {
return true;
}
}
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
index a013212..27247d4 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
@@ -19,11 +19,11 @@
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.MemberResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.NestHostClassAttribute;
import com.android.tools.r8.graph.NestMemberClassAttribute;
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.SuccessfulMemberResolutionResult;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -97,7 +97,7 @@
}
public ProgramMethod registerMethodReference(DexMethod method) {
- ResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method);
+ MethodResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method);
registerMemberAccess(resolutionResult, false);
return resolutionResult.isSingleResolution()
? resolutionResult.asSingleResolution().getResolvedProgramMethod()
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 ace24131..32654be 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -214,11 +214,13 @@
if (memberInfo.isSignatureAttributeRemovalAllowed(options)) {
member.clearGenericSignature();
}
- if (!member.getKotlinInfo().isProperty() && memberInfo.isKotlinMetadataRemovalAllowed(clazz)) {
+ if (!member.getKotlinInfo().isProperty()
+ && memberInfo.isKotlinMetadataRemovalAllowed(clazz, options)) {
member.clearKotlinInfo();
}
// Postpone removal of kotlin property info until we have seen all fields, setters and getters.
- if (member.getKotlinInfo().isProperty() && !memberInfo.isKotlinMetadataRemovalAllowed(clazz)) {
+ if (member.getKotlinInfo().isProperty()
+ && !memberInfo.isKotlinMetadataRemovalAllowed(clazz, options)) {
pinnedKotlinProperties.add(member.getKotlinInfo().asProperty());
}
}
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 68b7e13..aef58b7 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -6,7 +6,7 @@
import static com.android.tools.r8.graph.DexEncodedMethod.asProgramMethodOrNull;
import static com.android.tools.r8.graph.DexEncodedMethod.toMethodDefinitionOrNull;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-import static com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult.isOverriding;
+import static com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult.isOverriding;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
@@ -37,18 +37,20 @@
import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
import com.android.tools.r8.graph.LookupTarget;
import com.android.tools.r8.graph.MethodAccessInfoCollection;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
+import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
-import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
+import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper;
+import com.android.tools.r8.shaking.KeepInfo.Joiner;
import com.android.tools.r8.synthesis.CommittedItems;
import com.android.tools.r8.utils.CollectionUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -371,7 +373,7 @@
}
private boolean verify() {
- assert keepInfo.verifyPinnedTypesAreLive(liveTypes);
+ assert keepInfo.verifyPinnedTypesAreLive(liveTypes, options());
assert objectAllocationInfoCollection.verifyAllocatedTypesAreLive(
liveTypes, getMissingClasses(), this);
return true;
@@ -435,7 +437,7 @@
DexProgramClass clazz =
asProgramClassOrNull(previous.definitionFor(reference.asDexType()));
if (clazz != null) {
- collection.pinClass(clazz);
+ collection.joinClass(clazz, Joiner::disallowShrinking);
}
} else if (reference.isDexMethod()) {
DexMethod method = reference.asDexMethod();
@@ -443,7 +445,7 @@
if (clazz != null) {
ProgramMethod definition = clazz.lookupProgramMethod(method);
if (definition != null) {
- collection.pinMethod(definition);
+ collection.joinMethod(definition, Joiner::disallowShrinking);
}
}
} else {
@@ -452,7 +454,7 @@
if (clazz != null) {
ProgramField definition = clazz.lookupProgramField(field);
if (definition != null) {
- collection.pinField(definition);
+ collection.joinField(definition, Joiner::disallowShrinking);
}
}
}
@@ -519,8 +521,8 @@
|| deadProtoTypes.contains(type)
|| getMissingClasses().contains(type)
// TODO(b/150693139): Remove these exceptions once fixed.
- || InterfaceMethodRewriter.isCompanionClassType(type)
- || InterfaceMethodRewriter.isEmulatedLibraryClassType(type)
+ || InterfaceDesugaringSyntheticHelper.isCompanionClassType(type)
+ || InterfaceDesugaringSyntheticHelper.isEmulatedLibraryClassType(type)
// TODO(b/150736225): Not sure how to remove these.
|| DesugaredLibraryAPIConverter.isVivifiedType(type)
: "Failed lookup of non-missing type: " + type;
@@ -601,6 +603,10 @@
return methodsTargetedByInvokeDynamic.contains(method);
}
+ public boolean isMethodTargetedByInvokeDynamic(ProgramMethod method) {
+ return isMethodTargetedByInvokeDynamic(method.getReference());
+ }
+
public Set<DexMethod> getVirtualMethodsTargetedByInvokeDirect() {
return virtualMethodsTargetedByInvokeDirect;
}
@@ -807,6 +813,19 @@
return isInstantiatedDirectly(clazz) || isInstantiatedIndirectly(clazz);
}
+ public boolean isReachableOrReferencedField(DexEncodedField field) {
+ assert checkIfObsolete();
+ DexField reference = field.getReference();
+ FieldAccessInfo info = getFieldAccessInfoCollection().get(reference);
+ if (info != null) {
+ assert info.isRead() || info.isWritten();
+ return true;
+ }
+ // TODO(b/192924387): When we enqueue a field as a root item, we should maybe create a
+ // FieldAccessInfo that describes the field is read and written using reflection.
+ return !getKeepInfo().getFieldInfo(reference, this).isShrinkingAllowed(options());
+ }
+
public boolean isFieldRead(DexEncodedField encodedField) {
assert checkIfObsolete();
DexField field = encodedField.getReference();
@@ -814,7 +833,7 @@
if (info != null && info.isRead()) {
return true;
}
- if (keepInfo.isPinned(field, this)) {
+ if (isPinned(field)) {
return true;
}
// For library classes we don't know whether a field is read.
@@ -890,7 +909,7 @@
return method.getDefinition().hasCode()
&& !method.getDefinition().isLibraryMethodOverride().isPossiblyTrue()
&& !neverReprocess.contains(reference)
- && !keepInfo.getMethodInfo(method).isPinned();
+ && !keepInfo.getMethodInfo(method).isPinned(options());
}
public boolean mayPropagateValueFor(DexClassAndMember<?, ?> member) {
@@ -951,6 +970,10 @@
&& keepInfo.getInfo(reference, this).isMinificationAllowed(options());
}
+ public boolean isAccessModificationAllowed(ProgramDefinition definition) {
+ return isAccessModificationAllowed(definition.getReference());
+ }
+
public boolean isAccessModificationAllowed(DexReference reference) {
assert options().getProguardConfiguration().isAccessModificationAllowed();
return keepInfo.getInfo(reference, this).isAccessModificationAllowed(options());
@@ -960,7 +983,7 @@
if (!options().isRepackagingEnabled()) {
return false;
}
- if (!keepInfo.getInfo(clazz).isRepackagingAllowed(options())) {
+ if (!keepInfo.getInfo(clazz).isRepackagingAllowed(clazz, options())) {
return false;
}
for (DexType superType : clazz.allImmediateSupertypes()) {
@@ -971,7 +994,7 @@
return clazz
.traverseProgramMembers(
member -> {
- if (keepInfo.getInfo(member).isRepackagingAllowed(options())) {
+ if (keepInfo.getInfo(member).isRepackagingAllowed(member, options())) {
return TraversalContinuation.CONTINUE;
}
return TraversalContinuation.BREAK;
@@ -981,7 +1004,7 @@
public boolean isPinned(DexReference reference) {
assert checkIfObsolete();
- return keepInfo.isPinned(reference, this);
+ return keepInfo.isPinned(reference, this, options());
}
public boolean isPinned(DexDefinition definition) {
@@ -1070,7 +1093,7 @@
methodAccessInfoCollection.rewrittenWithLens(definitionSupplier, lens),
objectAllocationInfoCollection.rewrittenWithLens(definitionSupplier, lens),
lens.rewriteCallSites(callSites, definitionSupplier),
- keepInfo.rewrite(lens, application.options),
+ keepInfo.rewrite(definitionSupplier, lens, application.options),
// Take any rule in case of collisions.
lens.rewriteReferenceKeys(mayHaveSideEffects, ListUtils::first),
// Drop assume rules in case of collisions.
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultTreePrunerConfiguration.java b/src/main/java/com/android/tools/r8/shaking/DefaultTreePrunerConfiguration.java
index c33ec55..b02ea79 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultTreePrunerConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultTreePrunerConfiguration.java
@@ -19,6 +19,6 @@
@Override
public boolean isReachableOrReferencedField(AppInfoWithLiveness appInfo, DexEncodedField field) {
- return appInfo.isFieldRead(field) || appInfo.isFieldWritten(field);
+ return appInfo.isReachableOrReferencedField(field);
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/DependentMinimumKeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/DependentMinimumKeepInfoCollection.java
new file mode 100644
index 0000000..aca76da
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/DependentMinimumKeepInfoCollection.java
@@ -0,0 +1,166 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking;
+
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.shaking.EnqueuerEvent.ClassEnqueuerEvent;
+import com.android.tools.r8.shaking.EnqueuerEvent.UnconditionalKeepInfoEvent;
+import com.android.tools.r8.shaking.KeepInfo.Joiner;
+import com.android.tools.r8.utils.MapUtils;
+import com.android.tools.r8.utils.TriConsumer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
+public class DependentMinimumKeepInfoCollection {
+
+ private final Map<EnqueuerEvent, MinimumKeepInfoCollection> dependentMinimumKeepInfo;
+
+ public DependentMinimumKeepInfoCollection() {
+ this(new HashMap<>());
+ }
+
+ private DependentMinimumKeepInfoCollection(
+ Map<EnqueuerEvent, MinimumKeepInfoCollection> dependentMinimumKeepInfo) {
+ this.dependentMinimumKeepInfo = dependentMinimumKeepInfo;
+ }
+
+ public void forEach(BiConsumer<EnqueuerEvent, MinimumKeepInfoCollection> consumer) {
+ dependentMinimumKeepInfo.forEach(consumer);
+ }
+
+ public void forEach(
+ DexDefinitionSupplier definitions,
+ TriConsumer<EnqueuerEvent, DexProgramClass, KeepClassInfo.Joiner> classConsumer,
+ TriConsumer<EnqueuerEvent, ProgramField, KeepFieldInfo.Joiner> fieldConsumer,
+ TriConsumer<EnqueuerEvent, ProgramMethod, KeepMethodInfo.Joiner> methodConsumer) {
+ dependentMinimumKeepInfo.forEach(
+ (preconditionEvent, minimumKeepInfo) ->
+ minimumKeepInfo.forEach(
+ definitions,
+ (clazz, minimumKeepInfoForClass) ->
+ classConsumer.accept(preconditionEvent, clazz, minimumKeepInfoForClass),
+ (field, minimumKeepInfoForField) ->
+ fieldConsumer.accept(preconditionEvent, field, minimumKeepInfoForField),
+ (method, minimumKeepInfoForMethod) ->
+ methodConsumer.accept(preconditionEvent, method, minimumKeepInfoForMethod)));
+ }
+
+ public MinimumKeepInfoCollection get(EnqueuerEvent preconditionEvent) {
+ return dependentMinimumKeepInfo.get(preconditionEvent);
+ }
+
+ public MinimumKeepInfoCollection getOrCreateMinimumKeepInfoFor(EnqueuerEvent preconditionEvent) {
+ return dependentMinimumKeepInfo.computeIfAbsent(
+ preconditionEvent, ignoreKey(MinimumKeepInfoCollection::new));
+ }
+
+ public Joiner<?, ?, ?> getOrCreateMinimumKeepInfoFor(
+ EnqueuerEvent preconditionEvent, DexReference reference) {
+ return getOrCreateMinimumKeepInfoFor(preconditionEvent)
+ .getOrCreateMinimumKeepInfoFor(reference);
+ }
+
+ public MinimumKeepInfoCollection getOrCreateUnconditionalMinimumKeepInfo() {
+ return getOrCreateMinimumKeepInfoFor(UnconditionalKeepInfoEvent.get());
+ }
+
+ public Joiner<?, ?, ?> getOrCreateUnconditionalMinimumKeepInfoFor(DexReference reference) {
+ return getOrCreateMinimumKeepInfoFor(UnconditionalKeepInfoEvent.get(), reference);
+ }
+
+ public MinimumKeepInfoCollection getOrDefault(
+ EnqueuerEvent preconditionEvent, MinimumKeepInfoCollection defaultValue) {
+ return dependentMinimumKeepInfo.getOrDefault(preconditionEvent, defaultValue);
+ }
+
+ public MinimumKeepInfoCollection getUnconditionalMinimumKeepInfoOrDefault(
+ MinimumKeepInfoCollection defaultValue) {
+ return getOrDefault(UnconditionalKeepInfoEvent.get(), defaultValue);
+ }
+
+ public void merge(DependentMinimumKeepInfoCollection otherDependentMinimumKeepInfo) {
+ otherDependentMinimumKeepInfo.forEach(
+ (preconditionEvent, minimumKeepInfo) ->
+ getOrCreateMinimumKeepInfoFor(preconditionEvent).merge(minimumKeepInfo));
+ }
+
+ public void pruneDeadItems(DexDefinitionSupplier definitions, Enqueuer enqueuer) {
+ MapUtils.removeIf(
+ dependentMinimumKeepInfo,
+ (preconditionEvent, minimumKeepInfo) -> {
+ // Check if the precondition refers to a pruned type.
+ if (preconditionEvent.isClassEvent()) {
+ ClassEnqueuerEvent classPreconditionEvent = preconditionEvent.asClassEvent();
+ DexClass clazz = definitions.definitionFor(classPreconditionEvent.getType());
+ if (clazz == null || !enqueuer.isReachable(clazz)) {
+ return true;
+ }
+ } else {
+ assert preconditionEvent.isUnconditionalKeepInfoEvent();
+ }
+
+ // Prune the consequent minimum keep info.
+ assert !minimumKeepInfo.isEmpty();
+ minimumKeepInfo.pruneDeadItems(definitions, enqueuer);
+
+ // If the consequent minimum keep info ended up empty, then remove the preconditionEvent
+ // from the dependent minimum keep info collection.
+ return minimumKeepInfo.isEmpty();
+ });
+ }
+
+ public MinimumKeepInfoCollection remove(EnqueuerEvent preconditionEvent) {
+ return dependentMinimumKeepInfo.remove(preconditionEvent);
+ }
+
+ public KeepClassInfo.Joiner remove(EnqueuerEvent preconditionEvent, DexType clazz) {
+ return internalRemove(preconditionEvent, minimumKeepInfo -> minimumKeepInfo.remove(clazz));
+ }
+
+ public KeepFieldInfo.Joiner remove(EnqueuerEvent preconditionEvent, DexField field) {
+ return internalRemove(preconditionEvent, minimumKeepInfo -> minimumKeepInfo.remove(field));
+ }
+
+ public KeepMethodInfo.Joiner remove(EnqueuerEvent preconditionEvent, DexMethod method) {
+ return internalRemove(preconditionEvent, minimumKeepInfo -> minimumKeepInfo.remove(method));
+ }
+
+ private <J extends Joiner<?, ?, ?>> J internalRemove(
+ EnqueuerEvent preconditionEvent, Function<MinimumKeepInfoCollection, J> fn) {
+ MinimumKeepInfoCollection minimumKeepInfo = get(preconditionEvent);
+ if (minimumKeepInfo == null) {
+ return null;
+ }
+ J minimumKeepInfoForReference = fn.apply(minimumKeepInfo);
+ if (minimumKeepInfo.isEmpty()) {
+ remove(preconditionEvent);
+ }
+ return minimumKeepInfoForReference;
+ }
+
+ public DependentMinimumKeepInfoCollection rewrittenWithLens(GraphLens graphLens) {
+ DependentMinimumKeepInfoCollection rewrittenDependentMinimumKeepInfo =
+ new DependentMinimumKeepInfoCollection();
+ forEach(
+ (preconditionEvent, minimumKeepInfo) ->
+ rewrittenDependentMinimumKeepInfo
+ .getOrCreateMinimumKeepInfoFor(preconditionEvent.rewrittenWithLens(graphLens))
+ .merge(minimumKeepInfo.rewrittenWithLens(graphLens)));
+ return rewrittenDependentMinimumKeepInfo;
+ }
+}
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 645d5b9..ba6fb3b 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -6,12 +6,11 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.graph.FieldAccessInfoImpl.MISSING_FIELD_ACCESS_INFO;
import static com.android.tools.r8.ir.desugar.LambdaDescriptor.isLambdaMetafactoryMethod;
-import static com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.emulateInterfaceLibraryMethod;
-import static com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.getEmulateLibraryInterfaceClassType;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.emulateInterfaceLibraryMethod;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.getEmulateLibraryInterfaceClassType;
import static com.android.tools.r8.naming.IdentifierNameStringUtils.identifyIdentifier;
import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument;
-import static com.android.tools.r8.utils.MapUtils.ignoreKey;
import static java.util.Collections.emptySet;
import com.android.tools.r8.Diagnostic;
@@ -30,6 +29,7 @@
import com.android.tools.r8.graph.ClassDefinition;
import com.android.tools.r8.graph.ClasspathOrLibraryClass;
import com.android.tools.r8.graph.ClasspathOrLibraryDefinition;
+import com.android.tools.r8.graph.Definition;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotation.AnnotatedKind;
import com.android.tools.r8.graph.DexAnnotationSet;
@@ -67,21 +67,19 @@
import com.android.tools.r8.graph.LookupTarget;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.MethodAccessInfoCollection;
+import com.android.tools.r8.graph.MethodResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult.FailedResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.NestMemberClassAttribute;
import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramDerivedContext;
import com.android.tools.r8.graph.ProgramField;
-import com.android.tools.r8.graph.ProgramMember;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
-import com.android.tools.r8.graph.ResolutionResult.FailedResolutionResult;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.graph.UseRegistry.MethodHandleUse;
import com.android.tools.r8.graph.analysis.ApiModelAnalysis;
-import com.android.tools.r8.graph.analysis.DesugaredLibraryConversionWrapperAnalysis;
import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
import com.android.tools.r8.graph.analysis.EnqueuerCheckCastAnalysis;
import com.android.tools.r8.graph.analysis.EnqueuerExceptionGuardAnalysis;
@@ -122,8 +120,8 @@
import com.android.tools.r8.shaking.KeepInfoCollection.MutableKeepInfoCollection;
import com.android.tools.r8.shaking.RootSetUtils.ConsequentRootSet;
import com.android.tools.r8.shaking.RootSetUtils.ConsequentRootSetBuilder;
-import com.android.tools.r8.shaking.RootSetUtils.ItemsWithRules;
import com.android.tools.r8.shaking.RootSetUtils.RootSet;
+import com.android.tools.r8.shaking.RootSetUtils.RootSetBase;
import com.android.tools.r8.shaking.RootSetUtils.RootSetBuilder;
import com.android.tools.r8.shaking.ScopedDexMethodSet.AddMethodIfMoreVisibleResult;
import com.android.tools.r8.synthesis.SyntheticItems.SynthesizingContextOracle;
@@ -139,9 +137,7 @@
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.Visibility;
import com.android.tools.r8.utils.WorkList;
-import com.android.tools.r8.utils.collections.ProgramFieldMap;
import com.android.tools.r8.utils.collections.ProgramFieldSet;
-import com.android.tools.r8.utils.collections.ProgramMethodMap;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.ImmutableSet;
@@ -391,13 +387,8 @@
* Conditional minimum keep info for classes, fields, and methods, which should only be applied if
* the outermost {@link EnqueuerEvent} is triggered during tracing (e.g., class X becomes live).
*/
- private final Map<EnqueuerEvent, Map<DexProgramClass, KeepClassInfo.Joiner>>
- dependentMinimumKeepClassInfo = new HashMap<>();
-
- private final Map<EnqueuerEvent, ProgramFieldMap<KeepFieldInfo.Joiner>>
- dependentMinimumKeepFieldInfo = new HashMap<>();
- private final Map<EnqueuerEvent, ProgramMethodMap<KeepMethodInfo.Joiner>>
- dependentMinimumKeepMethodInfo = new HashMap<>();
+ private final DependentMinimumKeepInfoCollection dependentMinimumKeepInfo =
+ new DependentMinimumKeepInfoCollection();
/**
* A set of seen const-class references that serve as an initial lock-candidate set and will
@@ -439,7 +430,6 @@
private final GraphReporter graphReporter;
private final CfInstructionDesugaringCollection desugaring;
- private final DesugaredLibraryConversionWrapperAnalysis desugaredLibraryWrapperAnalysis;
private final ProgramMethodSet pendingDesugaring = ProgramMethodSet.create();
Enqueuer(
@@ -491,13 +481,6 @@
objectAllocationInfoCollection =
ObjectAllocationInfoCollectionImpl.builder(mode.isInitialTreeShaking(), graphReporter);
- if (appView.rewritePrefix.isRewriting() && mode.isInitialTreeShaking()) {
- desugaredLibraryWrapperAnalysis = new DesugaredLibraryConversionWrapperAnalysis(appView);
- registerAnalysis(desugaredLibraryWrapperAnalysis);
- registerInvokeAnalysis(desugaredLibraryWrapperAnalysis);
- } else {
- desugaredLibraryWrapperAnalysis = null;
- }
apiReferenceLevelCache = AndroidApiReferenceLevelCache.create(appView);
}
@@ -792,28 +775,43 @@
}
}
- private void enqueueRootItems(ItemsWithRules items) {
- items.forEachField(this::enqueueRootField);
- items.forEachMethod(this::enqueueRootMethod);
- items.forEachClass(this::enqueueRootClass);
- }
-
- // TODO(b/123923324): Verify that root items are present.
- private void enqueueRootClass(DexType type, Set<ProguardKeepRuleBase> rules) {
- DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(type));
- if (clazz != null) {
- enqueueRootClass(clazz, rules);
+ private void enqueueAllIfNotShrinking() {
+ if (appView.options().isShrinking()) {
+ return;
+ }
+ // Add everything if we are not shrinking.
+ assert appView.options().getProguardConfiguration().getKeepAllRule() != null;
+ ProguardKeepRuleBase keepAllRule =
+ appView.options().getProguardConfiguration().getKeepAllRule();
+ KeepClassInfo.Joiner keepClassInfo =
+ KeepClassInfo.newEmptyJoiner().addRule(keepAllRule).disallowShrinking();
+ KeepFieldInfo.Joiner keepFieldInfo =
+ KeepFieldInfo.newEmptyJoiner().addRule(keepAllRule).disallowShrinking();
+ KeepMethodInfo.Joiner keepMethodInfo =
+ KeepMethodInfo.newEmptyJoiner().addRule(keepAllRule).disallowShrinking();
+ EnqueuerEvent preconditionEvent = UnconditionalKeepInfoEvent.get();
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (appView.getSyntheticItems().isSyntheticClass(clazz)
+ && !appView.getSyntheticItems().isSubjectToKeepRules(clazz)) {
+ // Don't treat compiler synthesized classes as kept roots.
+ continue;
+ }
+ enqueueClassDueToNoShrinkingRule(clazz, keepClassInfo, preconditionEvent);
+ clazz.forEachProgramField(
+ field -> enqueueFieldDueToNoShrinkingRule(field, keepFieldInfo, preconditionEvent));
+ clazz.forEachProgramMethod(
+ method -> enqueueMethodDueToNoShrinkingRule(method, keepMethodInfo, preconditionEvent));
}
}
- private void enqueueRootClass(DexProgramClass clazz, Set<ProguardKeepRuleBase> rules) {
- enqueueRootClass(clazz, rules, null);
- }
-
- private void enqueueRootClass(
- DexProgramClass clazz, Set<ProguardKeepRuleBase> rules, DexDefinition precondition) {
- keepClassWithRules(clazz, rules);
- enqueueKeepRuleInstantiatedType(clazz, rules, precondition);
+ private void enqueueClassDueToNoShrinkingRule(
+ DexProgramClass clazz,
+ KeepClassInfo.Joiner minimumKeepInfo,
+ EnqueuerEvent preconditionEvent) {
+ assert !minimumKeepInfo.isShrinkingAllowed();
+ assert !minimumKeepInfo.getRules().isEmpty();
+ DexDefinition precondition = preconditionEvent.getDefinition(appInfo());
+ enqueueKeepRuleInstantiatedType(clazz, minimumKeepInfo.getRules(), precondition);
}
private void enqueueKeepRuleInstantiatedType(
@@ -839,63 +837,28 @@
}
}
- // TODO(b/123923324): Verify that root items are present.
- private void enqueueRootField(DexField reference, Set<ProguardKeepRuleBase> rules) {
- DexProgramClass holder =
- asProgramClassOrNull(appInfo().definitionFor(reference.getHolderType()));
- if (holder != null) {
- ProgramField field = holder.lookupProgramField(reference);
- if (field != null) {
- enqueueRootField(field, rules);
- }
- }
- }
-
- private void enqueueRootField(ProgramField field, Set<ProguardKeepRuleBase> rules) {
- enqueueRootField(field, rules, null);
- }
-
- private void enqueueRootField(
- ProgramField field, Set<ProguardKeepRuleBase> rules, DexDefinition precondition) {
- keepFieldWithRules(field, rules);
+ private void enqueueFieldDueToNoShrinkingRule(
+ ProgramField field, KeepFieldInfo.Joiner minimumKeepInfo, EnqueuerEvent preconditionEvent) {
+ assert !minimumKeepInfo.isShrinkingAllowed();
+ assert !minimumKeepInfo.getRules().isEmpty();
+ DexDefinition precondition = preconditionEvent.getDefinition(appInfo());
workList.enqueueMarkFieldKeptAction(
- field, graphReporter.reportKeepField(precondition, rules, field.getDefinition()));
+ field,
+ graphReporter.reportKeepField(
+ precondition, minimumKeepInfo.getRules(), field.getDefinition()));
}
- // TODO(b/123923324): Verify that root items are present.
- private void enqueueRootMethod(DexMethod reference, Set<ProguardKeepRuleBase> rules) {
- DexProgramClass holder =
- asProgramClassOrNull(appInfo().definitionFor(reference.getHolderType()));
- if (holder != null) {
- ProgramMethod method = holder.lookupProgramMethod(reference);
- if (method != null) {
- enqueueRootMethod(method, rules, null);
- }
- }
- }
-
- private void enqueueRootMethod(ProgramMethod method, Set<ProguardKeepRuleBase> rules) {
- enqueueRootMethod(method, rules, null);
- }
-
- private void enqueueRootMethod(
- ProgramMethod method, Set<ProguardKeepRuleBase> rules, DexDefinition precondition) {
- keepMethodWithRules(method, rules);
+ private void enqueueMethodDueToNoShrinkingRule(
+ ProgramMethod method,
+ KeepMethodInfo.Joiner minimumKeepInfo,
+ EnqueuerEvent preconditionEvent) {
+ assert !minimumKeepInfo.isShrinkingAllowed();
+ assert !minimumKeepInfo.getRules().isEmpty();
+ DexDefinition precondition = preconditionEvent.getDefinition(appInfo());
workList.enqueueMarkMethodKeptAction(
- method, graphReporter.reportKeepMethod(precondition, rules, method.getDefinition()));
- }
-
- private void internalEnqueueRootItem(
- ProgramDefinition item, Set<ProguardKeepRuleBase> rules, DexDefinition precondition) {
- if (item.isProgramClass()) {
- enqueueRootClass(item.asProgramClass(), rules, precondition);
- } else if (item.isProgramField()) {
- enqueueRootField(item.asProgramField(), rules, precondition);
- } else if (item.isProgramMethod()) {
- enqueueRootMethod(item.asProgramMethod(), rules, precondition);
- } else {
- throw new IllegalArgumentException(item.toString());
- }
+ method,
+ graphReporter.reportKeepMethod(
+ precondition, minimumKeepInfo.getRules(), method.getDefinition()));
}
private void enqueueFirstNonSerializableClassInitializer(
@@ -1861,9 +1824,6 @@
clazz, deferredParameterAnnotations, annotatedItem -> AnnotatedKind.PARAMETER);
}
- rootSet.forEachDependentInstanceConstructor(
- clazz, appView, this::enqueueHolderWithDependentInstanceConstructor);
- rootSet.forEachDependentStaticMember(clazz, appView, this::enqueueDependentMember);
compatEnqueueHolderIfDependentNonStaticMember(
clazz, rootSet.getDependentKeepClassCompatRule(clazz.getType()));
@@ -1956,13 +1916,6 @@
}
}
- private void enqueueDependentMember(
- DexDefinition precondition,
- ProgramMember<?, ?> consequent,
- Set<ProguardKeepRuleBase> reasons) {
- internalEnqueueRootItem(consequent, reasons, precondition);
- }
-
private void enqueueHolderWithDependentInstanceConstructor(
ProgramMethod instanceInitializer, Set<ProguardKeepRuleBase> reasons) {
DexProgramClass holder = instanceInitializer.getHolder();
@@ -2050,7 +2003,7 @@
DexMethod method, ProgramDefinition context, KeepReason reason) {
// Record the references in case they are not program types.
recordMethodReference(method, context);
- ResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method);
+ MethodResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method);
if (resolutionResult.isFailedResolution()) {
markFailedMethodResolutionTargets(
method, resolutionResult.asFailedResolution(), context, reason);
@@ -2061,7 +2014,7 @@
private SingleResolutionResult resolveMethod(
DexMethod method, ProgramDefinition context, KeepReason reason, boolean interfaceInvoke) {
// Record the references in case they are not program types.
- ResolutionResult resolutionResult = appInfo.resolveMethod(method, interfaceInvoke);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethod(method, interfaceInvoke);
if (resolutionResult.isSingleResolution()) {
recordMethodReference(
method, resolutionResult.getResolutionPair().asProgramDerivedContext(context));
@@ -2400,6 +2353,9 @@
void markAnnotationAsInstantiated(DexProgramClass clazz, KeepReasonWitness witness) {
assert clazz.isAnnotation();
+ if (!objectAllocationInfoCollection.recordInstantiatedAnnotation(clazz, appInfo)) {
+ return;
+ }
markTypeAsLive(clazz, witness);
transitionDependentItemsForInstantiatedInterface(clazz);
}
@@ -2516,7 +2472,8 @@
assert appInfo.isSubtype(currentClass.type, type);
instantiation.apply(subTypeConsumer, lambdaConsumer);
},
- definition -> keepInfo.isPinned(definition.getReference(), appInfo))
+ definition ->
+ keepInfo.isPinned(definition.getReference(), appInfo, options))
.forEach(
target ->
markVirtualDispatchTargetAsLive(
@@ -2569,7 +2526,7 @@
private void markLibraryOrClasspathOverrideLive(
InstantiatedObject instantiation,
DexClass libraryOrClasspathClass,
- ResolutionResult resolution) {
+ MethodResolutionResult resolution) {
LookupTarget lookup = resolution.lookupVirtualDispatchTarget(instantiation, appInfo);
if (lookup == null) {
return;
@@ -2649,7 +2606,6 @@
} else {
do {
// Handle keep rules that are dependent on the class being instantiated.
- rootSet.forEachDependentNonStaticMember(clazz, appView, this::enqueueDependentMember);
applyMinimumKeepInfoDependentOn(new InstantiatedClassEnqueuerEvent(clazz));
for (DexType interfaceType : clazz.getInterfaces()) {
@@ -2670,8 +2626,6 @@
while (interfacesToTransition.hasNext()) {
DexProgramClass interfaceClass = interfacesToTransition.next();
- rootSet.forEachDependentNonStaticMember(
- interfaceClass, appView, this::enqueueDependentMember);
applyMinimumKeepInfoDependentOn(new InstantiatedClassEnqueuerEvent(interfaceClass));
for (DexType indirectInterfaceType : interfaceClass.getInterfaces()) {
@@ -2723,9 +2677,6 @@
// Update keep info.
applyMinimumKeepInfo(field);
- // Add all dependent members to the workqueue.
- enqueueRootItems(rootSet.getDependentItems(field.getDefinition()));
-
// Notify analyses.
analyses.forEach(analysis -> analysis.processNewlyLiveField(field, context));
}
@@ -2796,6 +2747,10 @@
return info != null;
}
+ public boolean isFieldReferenced(ProgramField field) {
+ return isFieldReferenced(field.getDefinition());
+ }
+
public boolean isFieldLive(ProgramField field) {
return liveFields.contains(field);
}
@@ -2858,6 +2813,10 @@
return liveMethods.contains(method);
}
+ public boolean isMethodLive(ProgramMethod method) {
+ return isMethodLive(method.getDefinition());
+ }
+
public boolean isMethodTargeted(DexEncodedMethod method) {
return targetedMethods.contains(method);
}
@@ -2881,6 +2840,29 @@
return liveNonProgramTypes.contains(clazz);
}
+ public boolean isReachable(Definition definition) {
+ assert definition != null;
+
+ if (definition.isClass()) {
+ return isTypeLive(definition.asClass());
+ }
+
+ assert definition.isMember();
+
+ if (definition.getContextClass().isProgramClass()) {
+ if (definition.isField()) {
+ ProgramField field = definition.asProgramField();
+ return isFieldLive(field) || isFieldReferenced(field);
+ } else {
+ assert definition.isMethod();
+ ProgramMethod method = definition.asProgramMethod();
+ return isMethodLive(method) || isMethodTargeted(method);
+ }
+ }
+
+ return isNonProgramTypeLive(definition.getContextClass());
+ }
+
public void forAllLiveClasses(Consumer<DexProgramClass> consumer) {
liveTypes.getItems().forEach(consumer);
}
@@ -2958,7 +2940,7 @@
(type, subTypeConsumer, lambdaConsumer) ->
objectAllocationInfoCollection.forEachInstantiatedSubType(
type, subTypeConsumer, lambdaConsumer, appInfo),
- definition -> keepInfo.isPinned(definition.getReference(), appInfo))
+ definition -> keepInfo.isPinned(definition.getReference(), appInfo, options))
.forEach(
target ->
markVirtualDispatchTargetAsLive(
@@ -3027,7 +3009,9 @@
// TODO(sgjesse): Does this have to be enqueued as a root item? Right now it is done as the
// marking for not renaming it is in the root set.
workList.enqueueMarkMethodKeptAction(valuesMethod, reason);
- keepInfo.joinMethod(valuesMethod, joiner -> joiner.pin().disallowMinification());
+ keepInfo.joinMethod(
+ valuesMethod,
+ joiner -> joiner.disallowMinification().disallowOptimization().disallowShrinking());
shouldNotBeMinified(valuesMethod);
}
}
@@ -3083,7 +3067,7 @@
assert mode.isMainDexTracing();
this.rootSet = appView.getMainDexRootSet();
// Translate the result of root-set computation into enqueuer actions.
- enqueueRootItems(rootSet.noShrinking);
+ includeMinimumKeepInfo(rootSet);
trace(executorService, timing);
options.reporter.failIfPendingErrors();
// Calculate the automatic main dex list according to legacy multidex constraints.
@@ -3120,43 +3104,27 @@
}
// Transfer the minimum keep info from the root set into the Enqueuer state.
- rootSet.forEachMinimumKeepInfo(
- appView,
- this::recordDependentMinimumKeepInfo,
- this::recordDependentMinimumKeepInfo,
- this::recordDependentMinimumKeepInfo);
+ includeMinimumKeepInfo(rootSet);
if (mode.isInitialTreeShaking()) {
// This is simulating the effect of the "root set" applied rules.
// This is done only in the initial pass, in subsequent passes the "rules" are reapplied
// by iterating the instances.
} else if (appView.getKeepInfo() != null) {
+ EnqueuerEvent preconditionEvent = UnconditionalKeepInfoEvent.get();
appView
.getKeepInfo()
.forEachRuleInstance(
appView,
- this::applyMinimumKeepInfoWhenLive,
- this::applyMinimumKeepInfoWhenLive,
- this::applyMinimumKeepInfoWhenLiveOrTargeted);
+ (clazz, minimumKeepInfo) ->
+ applyMinimumKeepInfoWhenLive(clazz, preconditionEvent, minimumKeepInfo),
+ (field, minimumKeepInfo) ->
+ applyMinimumKeepInfoWhenLive(field, preconditionEvent, minimumKeepInfo),
+ (method, minimumKeepInfo) ->
+ applyMinimumKeepInfoWhenLiveOrTargeted(
+ method, preconditionEvent, minimumKeepInfo));
}
- if (appView.options().isShrinking() || appView.options().getProguardConfiguration() == null) {
- enqueueRootItems(rootSet.noShrinking);
- } else {
- // Add everything if we are not shrinking.
- assert appView.options().getProguardConfiguration().getKeepAllRule() != null;
- ImmutableSet<ProguardKeepRuleBase> keepAllSet =
- ImmutableSet.of(appView.options().getProguardConfiguration().getKeepAllRule());
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- if (appView.getSyntheticItems().isSyntheticClass(clazz)
- && !appView.getSyntheticItems().isSubjectToKeepRules(clazz)) {
- // Don't treat compiler synthesized classes as kept roots.
- continue;
- }
- enqueueRootClass(clazz, keepAllSet);
- clazz.forEachProgramMethod(method -> enqueueRootMethod(method, keepAllSet));
- clazz.forEachProgramField(field -> enqueueRootField(field, keepAllSet));
- }
- }
+ enqueueAllIfNotShrinking();
trace(executorService, timing);
options.reporter.failIfPendingErrors();
finalizeLibraryMethodOverrideInformation();
@@ -3175,26 +3143,48 @@
return createEnqueuerResult(appInfo);
}
+ private void includeMinimumKeepInfo(RootSetBase rootSet) {
+ rootSet
+ .getDependentMinimumKeepInfo()
+ .forEach(
+ appView,
+ this::recordDependentMinimumKeepInfo,
+ this::recordDependentMinimumKeepInfo,
+ this::recordDependentMinimumKeepInfo);
+ }
+
private void applyMinimumKeepInfo(DexProgramClass clazz) {
- Map<DexProgramClass, KeepClassInfo.Joiner> minimumKeepClassInfo =
- dependentMinimumKeepClassInfo.get(UnconditionalKeepInfoEvent.get());
- if (minimumKeepClassInfo != null) {
- KeepClassInfo.Joiner minimumKeepInfoForClass = minimumKeepClassInfo.remove(clazz);
- if (minimumKeepInfoForClass != null) {
- keepInfo.joinClass(clazz, info -> info.merge(minimumKeepInfoForClass));
- }
+ EnqueuerEvent preconditionEvent = UnconditionalKeepInfoEvent.get();
+ KeepClassInfo.Joiner minimumKeepInfoForClass =
+ dependentMinimumKeepInfo.remove(preconditionEvent, clazz.getType());
+ if (minimumKeepInfoForClass != null) {
+ keepInfo.joinClass(clazz, info -> info.merge(minimumKeepInfoForClass));
+ enqueueClassIfShrinkingIsDisallowed(clazz, preconditionEvent, minimumKeepInfoForClass);
}
}
private void applyMinimumKeepInfoWhenLive(
- DexProgramClass clazz, KeepClassInfo.Joiner minimumKeepInfo) {
+ DexProgramClass clazz,
+ EnqueuerEvent preconditionEvent,
+ KeepClassInfo.Joiner minimumKeepInfo) {
if (liveTypes.contains(clazz)) {
keepInfo.joinClass(clazz, info -> info.merge(minimumKeepInfo));
} else {
- dependentMinimumKeepClassInfo
- .computeIfAbsent(UnconditionalKeepInfoEvent.get(), ignoreKey(IdentityHashMap::new))
- .computeIfAbsent(clazz, ignoreKey(KeepClassInfo::newEmptyJoiner))
- .merge(minimumKeepInfo);
+ dependentMinimumKeepInfo
+ .getOrCreateUnconditionalMinimumKeepInfo()
+ .mergeMinimumKeepInfoFor(clazz.getType(), minimumKeepInfo);
+ }
+ enqueueClassIfShrinkingIsDisallowed(clazz, preconditionEvent, minimumKeepInfo);
+ }
+
+ private void enqueueClassIfShrinkingIsDisallowed(
+ DexProgramClass clazz,
+ EnqueuerEvent preconditionEvent,
+ KeepClassInfo.Joiner minimumKeepInfo) {
+ if ((options.isShrinking() || mode.isMainDexTracing())
+ && !minimumKeepInfo.isShrinkingAllowed()) {
+ assert !minimumKeepInfo.getRules().isEmpty();
+ enqueueClassDueToNoShrinkingRule(clazz, minimumKeepInfo, preconditionEvent);
}
}
@@ -3203,70 +3193,98 @@
DexProgramClass clazz,
KeepClassInfo.Joiner minimumKeepInfo) {
if (isPreconditionForMinimumKeepInfoSatisfied(preconditionEvent)) {
- applyMinimumKeepInfoWhenLive(clazz, minimumKeepInfo);
+ applyMinimumKeepInfoWhenLive(clazz, preconditionEvent, minimumKeepInfo);
} else {
- dependentMinimumKeepClassInfo
- .computeIfAbsent(preconditionEvent, ignoreKey(IdentityHashMap::new))
- .computeIfAbsent(clazz, ignoreKey(KeepClassInfo::newEmptyJoiner))
- .merge(minimumKeepInfo);
+ dependentMinimumKeepInfo
+ .getOrCreateMinimumKeepInfoFor(preconditionEvent)
+ .mergeMinimumKeepInfoFor(clazz.getType(), minimumKeepInfo);
+ }
+ if (preconditionEvent.isUnconditionalKeepInfoEvent()) {
+ enqueueClassIfShrinkingIsDisallowed(clazz, preconditionEvent, minimumKeepInfo);
}
}
private void applyMinimumKeepInfo(ProgramField field) {
- ProgramFieldMap<KeepFieldInfo.Joiner> minimumKeepFieldInfo =
- dependentMinimumKeepFieldInfo.get(UnconditionalKeepInfoEvent.get());
- if (minimumKeepFieldInfo != null) {
- KeepFieldInfo.Joiner minimumKeepInfoForField = minimumKeepFieldInfo.remove(field);
+ EnqueuerEvent preconditionEvent = UnconditionalKeepInfoEvent.get();
+ KeepFieldInfo.Joiner minimumKeepInfoForField =
+ dependentMinimumKeepInfo.remove(preconditionEvent, field.getReference());
if (minimumKeepInfoForField != null) {
keepInfo.joinField(field, info -> info.merge(minimumKeepInfoForField));
+ enqueueFieldIfShrinkingIsDisallowed(field, preconditionEvent, minimumKeepInfoForField);
}
- }
}
private void applyMinimumKeepInfoWhenLive(
- ProgramField field, KeepFieldInfo.Joiner minimumKeepInfo) {
+ ProgramField field, EnqueuerEvent preconditionEvent, KeepFieldInfo.Joiner minimumKeepInfo) {
if (liveFields.contains(field)) {
keepInfo.joinField(field, info -> info.merge(minimumKeepInfo));
} else {
- dependentMinimumKeepFieldInfo
- .computeIfAbsent(UnconditionalKeepInfoEvent.get(), ignoreKey(ProgramFieldMap::create))
- .computeIfAbsent(field, ignoreKey(KeepFieldInfo::newEmptyJoiner))
- .merge(minimumKeepInfo);
+ dependentMinimumKeepInfo
+ .getOrCreateUnconditionalMinimumKeepInfo()
+ .mergeMinimumKeepInfoFor(field.getReference(), minimumKeepInfo);
+ }
+ enqueueFieldIfShrinkingIsDisallowed(field, preconditionEvent, minimumKeepInfo);
+ }
+
+ private void enqueueFieldIfShrinkingIsDisallowed(
+ ProgramField field, EnqueuerEvent preconditionEvent, KeepFieldInfo.Joiner minimumKeepInfo) {
+ if ((options.isShrinking() || mode.isMainDexTracing())
+ && !minimumKeepInfo.isShrinkingAllowed()) {
+ assert !minimumKeepInfo.getRules().isEmpty();
+ enqueueFieldDueToNoShrinkingRule(field, minimumKeepInfo, preconditionEvent);
}
}
private void recordDependentMinimumKeepInfo(
EnqueuerEvent preconditionEvent, ProgramField field, KeepFieldInfo.Joiner minimumKeepInfo) {
if (isPreconditionForMinimumKeepInfoSatisfied(preconditionEvent)) {
- applyMinimumKeepInfoWhenLive(field, minimumKeepInfo);
+ applyMinimumKeepInfoWhenLive(field, preconditionEvent, minimumKeepInfo);
} else {
- dependentMinimumKeepFieldInfo
- .computeIfAbsent(preconditionEvent, ignoreKey(ProgramFieldMap::create))
- .computeIfAbsent(field, ignoreKey(KeepFieldInfo::newEmptyJoiner))
- .merge(minimumKeepInfo);
+ dependentMinimumKeepInfo
+ .getOrCreateMinimumKeepInfoFor(preconditionEvent)
+ .mergeMinimumKeepInfoFor(field.getReference(), minimumKeepInfo);
+ }
+ if (preconditionEvent.isUnconditionalKeepInfoEvent()) {
+ enqueueFieldIfShrinkingIsDisallowed(field, preconditionEvent, minimumKeepInfo);
}
}
private void applyMinimumKeepInfo(ProgramMethod method) {
- ProgramMethodMap<KeepMethodInfo.Joiner> minimumKeepMethodInfo =
- dependentMinimumKeepMethodInfo.get(UnconditionalKeepInfoEvent.get());
- if (minimumKeepMethodInfo != null) {
- KeepMethodInfo.Joiner minimumKeepInfoForMethod = minimumKeepMethodInfo.remove(method);
+ EnqueuerEvent preconditionEvent = UnconditionalKeepInfoEvent.get();
+ KeepMethodInfo.Joiner minimumKeepInfoForMethod =
+ dependentMinimumKeepInfo.remove(preconditionEvent, method.getReference());
if (minimumKeepInfoForMethod != null) {
keepInfo.joinMethod(method, info -> info.merge(minimumKeepInfoForMethod));
+ enqueueMethodIfShrinkingIsDisallowed(method, preconditionEvent, minimumKeepInfoForMethod);
}
- }
}
private void applyMinimumKeepInfoWhenLiveOrTargeted(
- ProgramMethod method, KeepMethodInfo.Joiner minimumKeepInfo) {
+ ProgramMethod method,
+ EnqueuerEvent preconditionEvent,
+ KeepMethodInfo.Joiner minimumKeepInfo) {
if (liveMethods.contains(method) || targetedMethods.contains(method)) {
keepInfo.joinMethod(method, info -> info.merge(minimumKeepInfo));
} else {
- dependentMinimumKeepMethodInfo
- .computeIfAbsent(UnconditionalKeepInfoEvent.get(), ignoreKey(ProgramMethodMap::create))
- .computeIfAbsent(method, ignoreKey(KeepMethodInfo::newEmptyJoiner))
- .merge(minimumKeepInfo);
+ dependentMinimumKeepInfo
+ .getOrCreateUnconditionalMinimumKeepInfo()
+ .mergeMinimumKeepInfoFor(method.getReference(), minimumKeepInfo);
+ }
+ enqueueMethodIfShrinkingIsDisallowed(method, preconditionEvent, minimumKeepInfo);
+ }
+
+ private void enqueueMethodIfShrinkingIsDisallowed(
+ ProgramMethod method,
+ EnqueuerEvent preconditionEvent,
+ KeepMethodInfo.Joiner minimumKeepInfo) {
+ if ((options.isShrinking() || mode.isMainDexTracing())
+ && !minimumKeepInfo.isShrinkingAllowed()) {
+ assert !minimumKeepInfo.getRules().isEmpty();
+ enqueueMethodDueToNoShrinkingRule(method, minimumKeepInfo, preconditionEvent);
+
+ if (method.getDefinition().isInstanceInitializer()) {
+ enqueueHolderWithDependentInstanceConstructor(method, minimumKeepInfo.getRules());
+ }
}
}
@@ -3275,72 +3293,31 @@
ProgramMethod method,
KeepMethodInfo.Joiner minimumKeepInfo) {
if (isPreconditionForMinimumKeepInfoSatisfied(preconditionEvent)) {
- applyMinimumKeepInfoWhenLiveOrTargeted(method, minimumKeepInfo);
+ applyMinimumKeepInfoWhenLiveOrTargeted(method, preconditionEvent, minimumKeepInfo);
} else {
- dependentMinimumKeepMethodInfo
- .computeIfAbsent(preconditionEvent, ignoreKey(ProgramMethodMap::create))
- .computeIfAbsent(method, ignoreKey(KeepMethodInfo::newEmptyJoiner))
- .merge(minimumKeepInfo);
+ dependentMinimumKeepInfo
+ .getOrCreateMinimumKeepInfoFor(preconditionEvent)
+ .mergeMinimumKeepInfoFor(method.getReference(), minimumKeepInfo);
+ }
+
+ if (preconditionEvent.isUnconditionalKeepInfoEvent()) {
+ enqueueMethodIfShrinkingIsDisallowed(method, preconditionEvent, minimumKeepInfo);
}
}
- private void applyMinimumKeepInfoDependentOn(EnqueuerEvent precondition) {
- Map<DexProgramClass, KeepClassInfo.Joiner> minimumKeepClassInfoDependentOnPrecondition =
- dependentMinimumKeepClassInfo.remove(precondition);
+ private void applyMinimumKeepInfoDependentOn(EnqueuerEvent preconditionEvent) {
+ MinimumKeepInfoCollection minimumKeepClassInfoDependentOnPrecondition =
+ dependentMinimumKeepInfo.remove(preconditionEvent);
if (minimumKeepClassInfoDependentOnPrecondition != null) {
- minimumKeepClassInfoDependentOnPrecondition.forEach(this::applyMinimumKeepInfoWhenLive);
- }
-
- ProgramFieldMap<KeepFieldInfo.Joiner> minimumKeepFieldInfoDependentOnPrecondition =
- dependentMinimumKeepFieldInfo.remove(precondition);
- if (minimumKeepFieldInfoDependentOnPrecondition != null) {
- minimumKeepFieldInfoDependentOnPrecondition.forEach(this::applyMinimumKeepInfoWhenLive);
- }
-
- ProgramMethodMap<KeepMethodInfo.Joiner> minimumKeepMethodInfoDependentOnPrecondition =
- dependentMinimumKeepMethodInfo.remove(precondition);
- if (minimumKeepMethodInfoDependentOnPrecondition != null) {
- minimumKeepMethodInfoDependentOnPrecondition.forEach(
- this::applyMinimumKeepInfoWhenLiveOrTargeted);
- }
- }
-
- private void keepClassWithRules(DexProgramClass clazz, Set<ProguardKeepRuleBase> rules) {
- keepInfo.joinClass(clazz, info -> applyKeepRules(clazz, rules, info));
- }
-
- private void keepFieldWithRules(ProgramField field, Set<ProguardKeepRuleBase> rules) {
- keepInfo.joinField(field, info -> applyKeepRules(field, rules, info));
- }
-
- private void keepMethodWithRules(ProgramMethod method, Set<ProguardKeepRuleBase> rules) {
- keepInfo.joinMethod(method, info -> applyKeepRules(method, rules, info));
- }
-
- private void applyKeepRules(
- ProgramDefinition definition,
- Set<ProguardKeepRuleBase> rules,
- KeepInfo.Joiner<?, ?, ?> joiner) {
- for (ProguardKeepRuleBase rule : rules) {
- ProguardKeepRuleModifiers modifiers =
- (rule.isProguardIfRule() ? rule.asProguardIfRule().getSubsequentRule() : rule)
- .getModifiers();
- if (!modifiers.allowsShrinking) {
- // TODO(b/159589281): Evaluate this interpretation.
- joiner.pin();
- if (definition.getAccessFlags().isPackagePrivateOrProtected()) {
- joiner.requireAccessModificationForRepackaging();
- }
- }
- if (!modifiers.allowsAccessModification) {
- joiner.disallowAccessModification();
- }
- if (!modifiers.allowsAnnotationRemoval) {
- joiner.disallowAnnotationRemoval();
- }
- if (!modifiers.allowsObfuscation) {
- joiner.disallowMinification();
- }
+ minimumKeepClassInfoDependentOnPrecondition.forEach(
+ appView,
+ (clazz, minimumKeepInfoForClass) ->
+ applyMinimumKeepInfoWhenLive(clazz, preconditionEvent, minimumKeepInfoForClass),
+ (field, minimumKeepInfoForField) ->
+ applyMinimumKeepInfoWhenLive(field, preconditionEvent, minimumKeepInfoForField),
+ (method, minimumKeepInfoForMethod) ->
+ applyMinimumKeepInfoWhenLiveOrTargeted(
+ method, preconditionEvent, minimumKeepInfoForMethod));
}
}
@@ -3452,7 +3429,6 @@
SyntheticAdditions additions = new SyntheticAdditions(appView.createProcessorContext());
desugar(additions);
synthesizeInterfaceMethodBridges(additions);
- synthesizeLibraryConversionWrappers(additions);
if (additions.isEmpty()) {
return;
}
@@ -3512,7 +3488,8 @@
DexProgramClass holder = bridge.getHolder();
DexEncodedMethod method = bridge.getDefinition();
holder.addVirtualMethod(method);
- additions.addLiveMethodWithKeepAction(bridge, KeepMethodInfo.Joiner::pin);
+ additions.addLiveMethodWithKeepAction(
+ bridge, joiner -> joiner.disallowOptimization().disallowShrinking());
}
syntheticInterfaceMethodBridges.clear();
}
@@ -3556,6 +3533,10 @@
// Prune the root set items that turned out to be dead.
// TODO(b/150736225): Pruning of dead root set items is still incomplete.
rootSet.pruneDeadItems(appView, this);
+ if (mode.isTreeShaking() && appView.hasMainDexRootSet()) {
+ assert rootSet != appView.getMainDexRootSet();
+ appView.getMainDexRootSet().pruneDeadItems(appView, this);
+ }
// Ensure references from all hard coded factory items.
appView
@@ -3763,20 +3744,6 @@
return synthesizedClasses;
}
- private void synthesizeLibraryConversionWrappers(SyntheticAdditions additions) {
- if (desugaredLibraryWrapperAnalysis == null) {
- return;
- }
-
- // Generate first the callbacks since they may require extra wrappers.
- ProgramMethodSet callbacks = desugaredLibraryWrapperAnalysis.generateCallbackMethods();
- additions.addLiveMethods(callbacks);
-
- // Generate wrappers on classpath so types are defined.
- desugaredLibraryWrapperAnalysis.generateWrappers(additions::addLiveClasspathClass);
- }
-
-
private static <D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
Set<R> toDescriptorSet(Set<D> set) {
ImmutableSet.Builder<R> builder = new ImmutableSet.Builder<>();
@@ -3831,7 +3798,7 @@
executorService,
activeIfRules,
consequentSetBuilder);
- addConsequentRootSet(ifRuleEvaluator.run(), false);
+ addConsequentRootSet(ifRuleEvaluator.run());
assert getNumberOfLiveItems() == numberOfLiveItemsAfterProcessing;
if (!workList.isEmpty()) {
continue;
@@ -3855,7 +3822,11 @@
continue;
}
- addConsequentRootSet(computeDelayedInterfaceMethodSyntheticBridges(), true);
+ ConsequentRootSet consequentRootSet = computeDelayedInterfaceMethodSyntheticBridges();
+ addConsequentRootSet(consequentRootSet);
+ rootSet
+ .getDependentMinimumKeepInfo()
+ .merge(consequentRootSet.getDependentMinimumKeepInfo());
rootSet.delayedRootSetActionItems.clear();
if (!workList.isEmpty()) {
@@ -3894,10 +3865,12 @@
SyntheticAdditions syntheticAdditions =
new SyntheticAdditions(appView.createProcessorContext());
+ assert workList.isEmpty();
+
R8PostProcessingDesugaringEventConsumer eventConsumer =
- CfPostProcessingDesugaringEventConsumer.createForR8(appView, syntheticAdditions);
+ CfPostProcessingDesugaringEventConsumer.createForR8(syntheticAdditions);
CfPostProcessingDesugaringCollection.create(appView, null, desugaring.getRetargetingInfo())
- .postProcessingDesugaring(eventConsumer, executorService);
+ .postProcessingDesugaring(liveTypes.items, eventConsumer, executorService);
if (syntheticAdditions.isEmpty()) {
return;
@@ -3910,7 +3883,7 @@
syntheticAdditions.enqueueWorkItems(this);
- workList = workList.nonPushable(syntheticAdditions.getLiveMethods());
+ workList = workList.nonPushable();
while (!workList.isEmpty()) {
EnqueuerAction action = workList.poll();
@@ -3927,42 +3900,12 @@
return result;
}
- private void addConsequentRootSet(ConsequentRootSet consequentRootSet, boolean addNoShrinking) {
- consequentRootSet.forEachClassWithDependentItems(
- appView,
- clazz -> {
- if (isTypeLive(clazz)) {
- consequentRootSet.forEachDependentInstanceConstructor(
- clazz, appView, this::enqueueHolderWithDependentInstanceConstructor);
- consequentRootSet.forEachDependentStaticMember(
- clazz, appView, this::enqueueDependentMember);
- if (objectAllocationInfoCollection.isInstantiatedDirectlyOrHasInstantiatedSubtype(
- clazz)) {
- consequentRootSet.forEachDependentNonStaticMember(
- clazz, appView, this::enqueueDependentMember);
- }
- compatEnqueueHolderIfDependentNonStaticMember(
- clazz, consequentRootSet.getDependentKeepClassCompatRule(clazz.type));
- }
- });
- consequentRootSet.forEachMemberWithDependentItems(
- appView,
- (member, dependentItems) -> {
- if (isMemberLive(member)) {
- enqueueRootItems(dependentItems);
- }
- });
-
- // TODO(b/132600955): This modifies the root set. Should the consequent be persistent?
- rootSet.addConsequentRootSet(consequentRootSet, addNoShrinking);
-
- enqueueRootItems(consequentRootSet.noShrinking);
-
- consequentRootSet.forEachMinimumKeepInfo(
- appView,
- this::recordDependentMinimumKeepInfo,
- this::recordDependentMinimumKeepInfo,
- this::recordDependentMinimumKeepInfo);
+ private void addConsequentRootSet(ConsequentRootSet consequentRootSet) {
+ // TODO(b/132600955): This modifies the root set, but the consequent should not be persistent.
+ // Instead, the consequent root set should be added to collections that are owned by the
+ // enqueuer, similar to Enqueuer#dependentMinimumKeepClassInfo.
+ rootSet.addConsequentRootSet(consequentRootSet);
+ includeMinimumKeepInfo(consequentRootSet);
// Check for compatibility rules indicating that the holder must be implicitly kept.
if (forceProguardCompatibility) {
@@ -3995,7 +3938,7 @@
ProgramMethod methodToKeep = action.getMethodToKeep();
ProgramMethod singleTarget = action.getSingleTarget();
DexEncodedMethod singleTargetMethod = singleTarget.getDefinition();
- if (rootSet.noShrinking.containsMethod(singleTarget.getReference())) {
+ if (rootSet.isShrinkingDisallowedUnconditionally(singleTarget, options)) {
return;
}
if (methodToKeep != singleTarget
@@ -4169,9 +4112,6 @@
}
}
- // Add all dependent members to the workqueue.
- enqueueRootItems(rootSet.getDependentItems(definition));
-
// Notify analyses.
analyses.forEach(analysis -> analysis.processNewlyLiveMethod(method, context));
}
@@ -4359,8 +4299,8 @@
}
// 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);
+ if (keepInfo.getClassInfo(clazz).isShrinkingAllowed(options)) {
+ keepInfo.joinClass(clazz, joiner -> joiner.disallowOptimization().disallowShrinking());
}
} else if (referencedItem.isDexField()) {
DexField field = referencedItem.asDexField();
@@ -4384,9 +4324,10 @@
workList.enqueueMarkInstantiatedAction(
clazz, null, InstantiationReason.REFLECTION, KeepReason.reflectiveUseIn(method));
}
- if (!keepInfo.getFieldInfo(encodedField, clazz).isPinned()) {
+ if (keepInfo.getFieldInfo(encodedField, clazz).isShrinkingAllowed(options)) {
ProgramField programField = new ProgramField(clazz, encodedField);
- keepInfo.pinField(programField);
+ keepInfo.joinField(
+ programField, joiner -> joiner.disallowOptimization().disallowShrinking());
markFieldAsKept(programField, KeepReason.reflectiveUseIn(method));
}
} else {
@@ -4575,7 +4516,7 @@
// Add this interface to the set of pinned items to ensure that we do not merge the
// interface into its unique subtype, if any.
// TODO(b/145344105): This should be superseded by the unknown interface hierarchy.
- keepInfo.pinClass(clazz);
+ keepInfo.joinClass(clazz, joiner -> joiner.disallowOptimization().disallowShrinking());
KeepReason reason = KeepReason.reflectiveUseIn(method);
markInterfaceAsInstantiated(clazz, graphReporter.registerClass(clazz, reason));
@@ -4583,7 +4524,8 @@
// illegal rewritings of invoke-interface instructions into invoke-virtual instructions.
clazz.forEachProgramVirtualMethod(
virtualMethod -> {
- keepInfo.pinMethod(virtualMethod);
+ keepInfo.joinMethod(
+ virtualMethod, joiner -> joiner.disallowOptimization().disallowShrinking());
markVirtualMethodAsReachable(virtualMethod.getReference(), true, clazz, reason);
});
}
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerEvent.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerEvent.java
index ba7ee40..ee2b7d0 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerEvent.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerEvent.java
@@ -4,11 +4,18 @@
package com.android.tools.r8.shaking;
+import com.android.tools.r8.graph.DexDefinition;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
public abstract class EnqueuerEvent {
+ public DexDefinition getDefinition(DexDefinitionSupplier definitions) {
+ return null;
+ }
+
public boolean isClassEvent() {
return false;
}
@@ -37,12 +44,19 @@
return false;
}
+ public abstract EnqueuerEvent rewrittenWithLens(GraphLens lens);
+
public abstract static class ClassEnqueuerEvent extends EnqueuerEvent {
private final DexType clazz;
- public ClassEnqueuerEvent(DexProgramClass clazz) {
- this.clazz = clazz.getType();
+ ClassEnqueuerEvent(DexType clazz) {
+ this.clazz = clazz;
+ }
+
+ @Override
+ public DexDefinition getDefinition(DexDefinitionSupplier definitions) {
+ return definitions.definitionFor(getType());
}
public DexType getType() {
@@ -63,7 +77,11 @@
public static class LiveClassEnqueuerEvent extends ClassEnqueuerEvent {
public LiveClassEnqueuerEvent(DexProgramClass clazz) {
- super(clazz);
+ this(clazz.getType());
+ }
+
+ private LiveClassEnqueuerEvent(DexType type) {
+ super(type);
}
@Override
@@ -77,6 +95,11 @@
}
@Override
+ public EnqueuerEvent rewrittenWithLens(GraphLens lens) {
+ return new LiveClassEnqueuerEvent(lens.lookupType(getType()));
+ }
+
+ @Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
@@ -97,7 +120,11 @@
public static class InstantiatedClassEnqueuerEvent extends ClassEnqueuerEvent {
public InstantiatedClassEnqueuerEvent(DexProgramClass clazz) {
- super(clazz);
+ this(clazz.getType());
+ }
+
+ private InstantiatedClassEnqueuerEvent(DexType type) {
+ super(type);
}
@Override
@@ -111,6 +138,11 @@
}
@Override
+ public EnqueuerEvent rewrittenWithLens(GraphLens lens) {
+ return new InstantiatedClassEnqueuerEvent(lens.lookupType(getType()));
+ }
+
+ @Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
@@ -142,5 +174,10 @@
public boolean isUnconditionalKeepInfoEvent() {
return true;
}
+
+ @Override
+ public EnqueuerEvent rewrittenWithLens(GraphLens lens) {
+ return this;
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
index 2fcac55..cd52fd7 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
@@ -17,7 +17,6 @@
import com.android.tools.r8.shaking.GraphReporter.KeepReasonWitness;
import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.collections.ProgramMethodSet;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
@@ -321,7 +320,7 @@
return queue.poll();
}
- abstract EnqueuerWorklist nonPushable(ProgramMethodSet enqueuedMarkMethodLive);
+ abstract EnqueuerWorklist nonPushable();
abstract boolean enqueueAssertAction(Action assertion);
@@ -373,8 +372,8 @@
}
@Override
- EnqueuerWorklist nonPushable(ProgramMethodSet enqueuedMarkMethodLive) {
- return new NonPushableEnqueuerWorklist(this, enqueuedMarkMethodLive);
+ EnqueuerWorklist nonPushable() {
+ return new NonPushableEnqueuerWorklist(this);
}
@Override
@@ -487,16 +486,12 @@
public static class NonPushableEnqueuerWorklist extends EnqueuerWorklist {
- private ProgramMethodSet enqueuedMarkMethodLive;
-
- private NonPushableEnqueuerWorklist(
- PushableEnqueuerWorkList workList, ProgramMethodSet enqueuedMarkMethodLive) {
+ private NonPushableEnqueuerWorklist(PushableEnqueuerWorkList workList) {
super(workList.enqueuer, workList.queue);
- this.enqueuedMarkMethodLive = enqueuedMarkMethodLive;
}
@Override
- EnqueuerWorklist nonPushable(ProgramMethodSet enqueuedMarkMethodLive) {
+ EnqueuerWorklist nonPushable() {
return this;
}
@@ -553,7 +548,7 @@
@Override
boolean enqueueMarkMethodLiveAction(
ProgramMethod method, ProgramDefinition context, KeepReason reason) {
- if (enqueuedMarkMethodLive.contains(method)) {
+ if (!enqueuer.addLiveMethod(method, reason)) {
return false;
}
throw attemptToEnqueue();
diff --git a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
index fabdd04..83055e7 100644
--- a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
+++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -27,7 +27,7 @@
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
+import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.utils.DequeUtils;
@@ -283,7 +283,7 @@
public KeepReasonWitness reportCompanionClass(DexProgramClass iface, DexProgramClass companion) {
assert iface.isInterface();
- assert InterfaceMethodRewriter.isCompanionClassType(companion.type);
+ assert InterfaceDesugaringSyntheticHelper.isCompanionClassType(companion.type);
if (keptGraphConsumer == null) {
return KeepReasonWitness.INSTANCE;
}
@@ -293,7 +293,7 @@
public KeepReasonWitness reportCompanionMethod(
DexEncodedMethod definition, DexEncodedMethod implementation) {
- assert InterfaceMethodRewriter.isCompanionClassType(implementation.getHolderType());
+ assert InterfaceDesugaringSyntheticHelper.isCompanionClassType(implementation.getHolderType());
if (keptGraphConsumer == null) {
return KeepReasonWitness.INSTANCE;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
index dc4a931..bf969b7 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
@@ -3,11 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
-import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.utils.InternalOptions;
import java.util.function.Function;
/** Immutable keep requirements for a class. */
@@ -46,36 +47,32 @@
}
@Override
- public boolean isRepackagingAllowed(GlobalKeepInfoConfiguration configuration) {
+ public boolean isRepackagingAllowed(
+ ProgramDefinition definition, GlobalKeepInfoConfiguration configuration) {
return configuration.isRepackagingEnabled()
&& internalIsMinificationAllowed()
- && !internalIsAccessModificationRequiredForRepackaging();
+ && (definition.getAccessFlags().isPublic()
+ || !internalIsAccessModificationRequiredForRepackaging());
}
public boolean isKotlinMetadataRemovalAllowed(
GlobalKeepInfoConfiguration configuration, boolean kotlinMetadataKept) {
return !kotlinMetadataKept
- || !isPinned()
+ || !isPinned(configuration)
|| !configuration.isKeepRuntimeVisibleAnnotationsEnabled()
|| isAnnotationRemovalAllowed(configuration);
}
- public static boolean isKotlinMetadataClassKept(AppView<?> appView) {
- return isKotlinMetadataClassKept(
- appView.dexItemFactory(),
- appView.appInfo()::definitionForWithoutExistenceAssert,
- appView.getKeepInfo()::getClassInfo);
- }
-
public static boolean isKotlinMetadataClassKept(
DexItemFactory factory,
+ InternalOptions options,
Function<DexType, DexClass> definitionForWithoutExistenceAssert,
Function<DexProgramClass, KeepClassInfo> getClassInfo) {
DexType kotlinMetadataType = factory.kotlinMetadataType;
DexClass kotlinMetadataClass = definitionForWithoutExistenceAssert.apply(kotlinMetadataType);
return kotlinMetadataClass == null
|| kotlinMetadataClass.isNotProgramClass()
- || getClassInfo.apply(kotlinMetadataClass.asProgramClass()).isPinned();
+ || !getClassInfo.apply(kotlinMetadataClass.asProgramClass()).isShrinkingAllowed(options);
}
@Override
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 b795aab..c3165ad 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
@@ -4,14 +4,17 @@
package com.android.tools.r8.shaking;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.EnclosingMethodAttribute;
+import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.shaking.KeepInfo.Builder;
+import com.google.common.collect.Sets;
+import java.util.Set;
import java.util.function.Consumer;
/** Keep information that can be associated with any item, i.e., class, method or field. */
public abstract class KeepInfo<B extends Builder<B, K>, K extends KeepInfo<B, K>> {
- private final boolean pinned;
private final boolean allowAccessModification;
private final boolean allowAnnotationRemoval;
private final boolean allowMinification;
@@ -20,14 +23,12 @@
private final boolean requireAccessModificationForRepackaging;
private KeepInfo(
- boolean pinned,
boolean allowAccessModification,
boolean allowAnnotationRemoval,
boolean allowMinification,
boolean allowOptimization,
boolean allowShrinking,
boolean requireAccessModificationForRepackaging) {
- this.pinned = pinned;
this.allowAccessModification = allowAccessModification;
this.allowAnnotationRemoval = allowAnnotationRemoval;
this.allowMinification = allowMinification;
@@ -38,7 +39,6 @@
KeepInfo(B builder) {
this(
- builder.isPinned(),
builder.isAccessModificationAllowed(),
builder.isAnnotationRemovalAllowed(),
builder.isMinificationAllowed(),
@@ -47,6 +47,13 @@
builder.isAccessModificationRequiredForRepackaging());
}
+ public static Joiner<?, ?, ?> newEmptyJoinerFor(DexReference reference) {
+ return reference.apply(
+ clazz -> KeepClassInfo.newEmptyJoiner(),
+ field -> KeepFieldInfo.newEmptyJoiner(),
+ method -> KeepMethodInfo.newEmptyJoiner());
+ }
+
abstract B builder();
/**
@@ -69,8 +76,8 @@
* @deprecated Prefer task dependent predicates.
*/
@Deprecated
- public boolean isPinned() {
- return pinned || !allowOptimization;
+ public boolean isPinned(GlobalKeepInfoConfiguration configuration) {
+ return !isOptimizationAllowed(configuration) || !isShrinkingAllowed(configuration);
}
/**
@@ -121,7 +128,8 @@
* <p>This method requires knowledge of the global configuration as that can override the concrete
* value on a given item.
*/
- public abstract boolean isRepackagingAllowed(GlobalKeepInfoConfiguration configuration);
+ public abstract boolean isRepackagingAllowed(
+ ProgramDefinition definition, GlobalKeepInfoConfiguration configuration);
boolean internalIsAccessModificationRequiredForRepackaging() {
return requireAccessModificationForRepackaging;
@@ -148,7 +156,7 @@
if (!configuration.isKeepAttributesSignatureEnabled()) {
return true;
}
- return !(configuration.isForceProguardCompatibilityEnabled() || isPinned());
+ return !(configuration.isForceProguardCompatibilityEnabled() || isPinned(configuration));
}
public boolean isEnclosingMethodAttributeRemovalAllowed(
@@ -161,14 +169,14 @@
if (configuration.isForceProguardCompatibilityEnabled()) {
return false;
}
- return !isPinned() || !enclosingMethodAttribute.isEnclosingPinned(appView);
+ return !isPinned(configuration) || !enclosingMethodAttribute.isEnclosingPinned(appView);
}
public boolean isInnerClassesAttributeRemovalAllowed(GlobalKeepInfoConfiguration configuration) {
if (!configuration.isKeepInnerClassesAttributeEnabled()) {
return true;
}
- return !(configuration.isForceProguardCompatibilityEnabled() || isPinned());
+ return !(configuration.isForceProguardCompatibilityEnabled() || isPinned(configuration));
}
public boolean isInnerClassesAttributeRemovalAllowed(
@@ -182,7 +190,7 @@
}
// The inner class is dependent on the enclosingMethodAttribute and since it has been pruned
// we can also remove this inner class relationship.
- return enclosingMethodAttribute == null || !isPinned();
+ return enclosingMethodAttribute == null || !isPinned(configuration);
}
public abstract boolean isTop();
@@ -192,8 +200,7 @@
public boolean isLessThanOrEquals(K other) {
// An item is less, aka, lower in the lattice, if each of its attributes is at least as
// permissive of that on other.
- return (!pinned || other.isPinned())
- && (allowAccessModification || !other.internalIsAccessModificationAllowed())
+ return (allowAccessModification || !other.internalIsAccessModificationAllowed())
&& (allowAnnotationRemoval || !other.internalIsAnnotationRemovalAllowed())
&& (allowMinification || !other.internalIsMinificationAllowed())
&& (allowOptimization || !other.internalIsOptimizationAllowed())
@@ -214,7 +221,6 @@
abstract boolean isEqualTo(K other);
private K original;
- private boolean pinned;
private boolean allowAccessModification;
private boolean allowAnnotationRemoval;
private boolean allowMinification;
@@ -228,7 +234,6 @@
Builder(K original) {
this.original = original;
- pinned = original.isPinned();
allowAccessModification = original.internalIsAccessModificationAllowed();
allowAnnotationRemoval = original.internalIsAnnotationRemovalAllowed();
allowMinification = original.internalIsMinificationAllowed();
@@ -239,7 +244,6 @@
}
B makeTop() {
- pin();
disallowAccessModification();
disallowAnnotationRemoval();
disallowMinification();
@@ -250,7 +254,6 @@
}
B makeBottom() {
- unpin();
allowAccessModification();
allowAnnotationRemoval();
allowMinification();
@@ -276,8 +279,7 @@
}
boolean internalIsEqualTo(K other) {
- return isPinned() == other.isPinned()
- && isAccessModificationAllowed() == other.internalIsAccessModificationAllowed()
+ return isAccessModificationAllowed() == other.internalIsAccessModificationAllowed()
&& isAnnotationRemovalAllowed() == other.internalIsAnnotationRemovalAllowed()
&& isMinificationAllowed() == other.internalIsMinificationAllowed()
&& isOptimizationAllowed() == other.internalIsOptimizationAllowed()
@@ -286,10 +288,6 @@
== other.internalIsAccessModificationRequiredForRepackaging();
}
- public boolean isPinned() {
- return pinned;
- }
-
public boolean isAccessModificationRequiredForRepackaging() {
return requireAccessModificationForRepackaging;
}
@@ -314,19 +312,6 @@
return allowShrinking;
}
- public B setPinned(boolean pinned) {
- this.pinned = pinned;
- return self();
- }
-
- public B pin() {
- return setPinned(true);
- }
-
- public B unpin() {
- return setPinned(false);
- }
-
public B setAllowMinification(boolean allowMinification) {
this.allowMinification = allowMinification;
return self();
@@ -415,6 +400,11 @@
final Builder<B, K> builder;
+ // The set of rules that have contributed to this joiner. These are only needed for the
+ // interpretation of keep rules into keep info, and is therefore not stored in the keep info
+ // builder above.
+ final Set<ProguardKeepRuleBase> rules = Sets.newIdentityHashSet();
+
Joiner(Builder<B, K> builder) {
this.builder = builder;
}
@@ -438,10 +428,18 @@
return null;
}
+ public Set<ProguardKeepRuleBase> getRules() {
+ return rules;
+ }
+
public boolean isBottom() {
return builder.isEqualTo(builder.getBottomInfo());
}
+ public boolean isShrinkingAllowed() {
+ return builder.isShrinkingAllowed();
+ }
+
public boolean isTop() {
return builder.isEqualTo(builder.getTopInfo());
}
@@ -451,8 +449,8 @@
return self();
}
- public J pin() {
- builder.pin();
+ public J addRule(ProguardKeepRuleBase rule) {
+ rules.add(rule);
return self();
}
@@ -488,7 +486,6 @@
public J merge(J joiner) {
Builder<B, K> builder = joiner.builder;
- applyIf(builder.isPinned(), Joiner::pin);
applyIf(!builder.isAccessModificationAllowed(), Joiner::disallowAccessModification);
applyIf(!builder.isAnnotationRemovalAllowed(), Joiner::disallowAnnotationRemoval);
applyIf(!builder.isMinificationAllowed(), Joiner::disallowMinification);
@@ -497,9 +494,15 @@
applyIf(
builder.isAccessModificationRequiredForRepackaging(),
Joiner::requireAccessModificationForRepackaging);
+ rules.addAll(joiner.rules);
return self();
}
+ @SuppressWarnings("unchecked")
+ public J mergeUnsafe(Joiner<?, ?, ?> joiner) {
+ return merge((J) joiner);
+ }
+
public K join() {
K joined = builder.build();
K original = builder.original;
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
index 71068d2..0dcddda 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -152,20 +152,30 @@
throw new Unreachable();
}
- public final boolean isPinned(DexReference reference, DexDefinitionSupplier definitions) {
- return getInfo(reference, definitions).isPinned();
+ public final boolean isPinned(
+ DexReference reference,
+ DexDefinitionSupplier definitions,
+ GlobalKeepInfoConfiguration configuration) {
+ return getInfo(reference, definitions).isPinned(configuration);
}
- public final boolean isPinned(DexType type, DexDefinitionSupplier definitions) {
- return getClassInfo(type, definitions).isPinned();
+ public final boolean isPinned(
+ DexType type, DexDefinitionSupplier definitions, GlobalKeepInfoConfiguration configuration) {
+ return getClassInfo(type, definitions).isPinned(configuration);
}
- public final boolean isPinned(DexMethod method, DexDefinitionSupplier definitions) {
- return getMethodInfo(method, definitions).isPinned();
+ public final boolean isPinned(
+ DexMethod method,
+ DexDefinitionSupplier definitions,
+ GlobalKeepInfoConfiguration configuration) {
+ return getMethodInfo(method, definitions).isPinned(configuration);
}
- public final boolean isPinned(DexField field, DexDefinitionSupplier definitions) {
- return getFieldInfo(field, definitions).isPinned();
+ public final boolean isPinned(
+ DexField field,
+ DexDefinitionSupplier definitions,
+ GlobalKeepInfoConfiguration configuration) {
+ return getFieldInfo(field, definitions).isPinned(configuration);
}
public final boolean isMinificationAllowed(
@@ -176,21 +186,22 @@
&& getInfo(reference, definitions).isMinificationAllowed(configuration);
}
- public abstract boolean verifyPinnedTypesAreLive(Set<DexType> liveTypes);
+ public abstract boolean verifyPinnedTypesAreLive(Set<DexType> liveTypes, InternalOptions options);
// TODO(b/156715504): We should try to avoid the need for iterating pinned items.
@Deprecated
- public abstract void forEachPinnedType(Consumer<DexType> consumer);
+ public abstract void forEachPinnedType(Consumer<DexType> consumer, InternalOptions options);
// TODO(b/156715504): We should try to avoid the need for iterating pinned items.
@Deprecated
- public abstract void forEachPinnedMethod(Consumer<DexMethod> consumer);
+ public abstract void forEachPinnedMethod(Consumer<DexMethod> consumer, InternalOptions options);
// TODO(b/156715504): We should try to avoid the need for iterating pinned items.
@Deprecated
- public abstract void forEachPinnedField(Consumer<DexField> consumer);
+ public abstract void forEachPinnedField(Consumer<DexField> consumer, InternalOptions options);
- public abstract KeepInfoCollection rewrite(NonIdentityGraphLens lens, InternalOptions options);
+ public abstract KeepInfoCollection rewrite(
+ DexDefinitionSupplier definitions, NonIdentityGraphLens lens, InternalOptions options);
public abstract KeepInfoCollection mutate(Consumer<MutableKeepInfoCollection> mutator);
@@ -240,15 +251,17 @@
}
@Override
- public KeepInfoCollection rewrite(NonIdentityGraphLens lens, InternalOptions options) {
+ public KeepInfoCollection rewrite(
+ DexDefinitionSupplier definitions, NonIdentityGraphLens lens, InternalOptions options) {
Map<DexType, KeepClassInfo> newClassInfo = new IdentityHashMap<>(keepClassInfo.size());
keepClassInfo.forEach(
(type, info) -> {
DexType newType = lens.lookupType(type);
assert newType == type
- || !info.isPinned()
+ || !info.isPinned(options)
|| info.isMinificationAllowed(options)
- || info.isRepackagingAllowed(options);
+ || info.isRepackagingAllowed(
+ definitions.definitionFor(newType).asProgramClass(), options);
KeepClassInfo previous = newClassInfo.put(newType, info);
assert previous == null;
});
@@ -256,17 +269,17 @@
keepMethodInfo.forEach(
(method, info) -> {
DexMethod newMethod = lens.getRenamedMethodSignature(method);
- assert !info.isPinned()
+ assert !info.isPinned(options)
|| info.isMinificationAllowed(options)
|| newMethod.name == method.name;
- assert !info.isPinned() || newMethod.getArity() == method.getArity();
- assert !info.isPinned()
+ assert !info.isPinned(options) || newMethod.getArity() == method.getArity();
+ assert !info.isPinned(options)
|| Streams.zip(
newMethod.getParameters().stream(),
method.getParameters().stream().map(lens::lookupType),
Object::equals)
.allMatch(x -> x);
- assert !info.isPinned()
+ assert !info.isPinned(options)
|| newMethod.getReturnType() == lens.lookupType(method.getReturnType());
KeepMethodInfo previous = newMethodInfo.put(newMethod, info);
// TODO(b/169927809): Avoid collisions.
@@ -277,7 +290,7 @@
(field, info) -> {
DexField newField = lens.getRenamedFieldSignature(field);
assert newField.name == field.name
- || !info.isPinned()
+ || !info.isPinned(options)
|| info.isMinificationAllowed(options);
KeepFieldInfo previous = newFieldInfo.put(newField, info);
assert previous == null;
@@ -312,7 +325,8 @@
IdentityHashMap::new,
rewriter,
Function.identity(),
- (joiner, otherJoiner) -> newEmptyJoiner.get().merge(joiner).merge(otherJoiner));
+ (reference, joiner, otherJoiner) ->
+ newEmptyJoiner.get().merge(joiner).merge(otherJoiner));
}
@Override
@@ -410,10 +424,6 @@
joinClass(clazz, KeepInfo.Joiner::top);
}
- public void pinClass(DexProgramClass clazz) {
- joinClass(clazz, KeepInfo.Joiner::pin);
- }
-
public void joinMethod(ProgramMethod method, Consumer<? super KeepMethodInfo.Joiner> fn) {
KeepMethodInfo info = getMethodInfo(method);
if (info.isTop()) {
@@ -432,19 +442,6 @@
joinMethod(method, KeepInfo.Joiner::top);
}
- public void pinMethod(ProgramMethod method) {
- joinMethod(method, KeepInfo.Joiner::pin);
- }
-
- // TODO(b/157700141): Avoid pinning/unpinning references.
- @Deprecated
- public void unsafeUnpinMethod(DexMethod method) {
- KeepMethodInfo info = keepMethodInfo.get(method);
- if (info != null && info.isPinned()) {
- keepMethodInfo.put(method, info.builder().unpin().build());
- }
- }
-
public void unsetRequireAllowAccessModificationForRepackaging(ProgramDefinition definition) {
if (definition.isProgramClass()) {
DexProgramClass clazz = definition.asProgramClass();
@@ -486,10 +483,6 @@
joinField(field, KeepInfo.Joiner::top);
}
- public void pinField(ProgramField field) {
- joinField(field, KeepInfo.Joiner::pin);
- }
-
@Override
public KeepInfoCollection mutate(Consumer<MutableKeepInfoCollection> mutator) {
mutator.accept(this);
@@ -497,39 +490,39 @@
}
@Override
- public boolean verifyPinnedTypesAreLive(Set<DexType> liveTypes) {
+ public boolean verifyPinnedTypesAreLive(Set<DexType> liveTypes, InternalOptions options) {
keepClassInfo.forEach(
(type, info) -> {
- assert !info.isPinned() || liveTypes.contains(type);
+ assert !info.isPinned(options) || liveTypes.contains(type);
});
return true;
}
@Override
- public void forEachPinnedType(Consumer<DexType> consumer) {
+ public void forEachPinnedType(Consumer<DexType> consumer, InternalOptions options) {
keepClassInfo.forEach(
(type, info) -> {
- if (info.isPinned()) {
+ if (info.isPinned(options)) {
consumer.accept(type);
}
});
}
@Override
- public void forEachPinnedMethod(Consumer<DexMethod> consumer) {
+ public void forEachPinnedMethod(Consumer<DexMethod> consumer, InternalOptions options) {
keepMethodInfo.forEach(
(method, info) -> {
- if (info.isPinned()) {
+ if (info.isPinned(options)) {
consumer.accept(method);
}
});
}
@Override
- public void forEachPinnedField(Consumer<DexField> consumer) {
+ public void forEachPinnedField(Consumer<DexField> consumer, InternalOptions options) {
keepFieldInfo.forEach(
(field, info) -> {
- if (info.isPinned()) {
+ if (info.isPinned(options)) {
consumer.accept(field);
}
});
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java
index 46d8fa2..2225e14 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.shaking;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.shaking.KeepInfo.Builder;
/** Immutable keep requirements for a member. */
@@ -15,14 +16,17 @@
}
@Override
- public boolean isRepackagingAllowed(GlobalKeepInfoConfiguration configuration) {
+ public boolean isRepackagingAllowed(
+ ProgramDefinition definition, GlobalKeepInfoConfiguration configuration) {
return configuration.isRepackagingEnabled()
- && !internalIsAccessModificationRequiredForRepackaging();
+ && (definition.getAccessFlags().isPublic()
+ || !internalIsAccessModificationRequiredForRepackaging());
}
- public boolean isKotlinMetadataRemovalAllowed(DexProgramClass holder) {
+ public boolean isKotlinMetadataRemovalAllowed(
+ DexProgramClass holder, GlobalKeepInfoConfiguration configuration) {
// Checking the holder for missing kotlin information relies on the holder being processed
// before members.
- return holder.getKotlinInfo().isNoKotlinInformation() || !isPinned();
+ return holder.getKotlinInfo().isNoKotlinInformation() || !isPinned(configuration);
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
index 1669a40..dd4226c 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
@@ -35,6 +35,10 @@
return new Builder(this);
}
+ public boolean isArgumentPropagationAllowed(GlobalKeepInfoConfiguration configuration) {
+ return isOptimizationAllowed(configuration);
+ }
+
public Joiner joiner() {
assert !isTop();
return new Joiner(this);
diff --git a/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java b/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java
index d63c685..763f7d4 100644
--- a/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java
+++ b/src/main/java/com/android/tools/r8/shaking/LibraryMethodOverrideAnalysis.java
@@ -53,7 +53,8 @@
appView
.appInfo()
.getKeepInfo()
- .forEachPinnedType(initialNonEscapingClassesWithLibraryMethodOverrides::remove);
+ .forEachPinnedType(
+ initialNonEscapingClassesWithLibraryMethodOverrides::remove, appView.options());
return initialNonEscapingClassesWithLibraryMethodOverrides;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/MinimumKeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/MinimumKeepInfoCollection.java
new file mode 100644
index 0000000..9f97e63
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/MinimumKeepInfoCollection.java
@@ -0,0 +1,172 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking;
+
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
+import com.android.tools.r8.graph.DexDefinitionSupplier;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.shaking.KeepInfo.Joiner;
+import com.android.tools.r8.utils.MapUtils;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.BiPredicate;
+import java.util.function.Predicate;
+
+public class MinimumKeepInfoCollection {
+
+ private static final MinimumKeepInfoCollection EMPTY =
+ new MinimumKeepInfoCollection(Collections.emptyMap());
+
+ private final Map<DexReference, KeepInfo.Joiner<?, ?, ?>> minimumKeepInfo;
+
+ public MinimumKeepInfoCollection() {
+ this(new IdentityHashMap<>());
+ }
+
+ private MinimumKeepInfoCollection(Map<DexReference, KeepInfo.Joiner<?, ?, ?>> minimumKeepInfo) {
+ this.minimumKeepInfo = minimumKeepInfo;
+ }
+
+ public static MinimumKeepInfoCollection empty() {
+ return EMPTY;
+ }
+
+ public void forEach(BiConsumer<DexReference, KeepInfo.Joiner<?, ?, ?>> consumer) {
+ minimumKeepInfo.forEach(consumer);
+ }
+
+ public void forEach(
+ DexDefinitionSupplier definitions,
+ BiConsumer<DexProgramClass, KeepClassInfo.Joiner> classConsumer,
+ BiConsumer<ProgramField, KeepFieldInfo.Joiner> fieldConsumer,
+ BiConsumer<ProgramMethod, KeepMethodInfo.Joiner> methodConsumer) {
+ minimumKeepInfo.forEach(
+ (reference, joiner) -> {
+ DexProgramClass contextClass =
+ asProgramClassOrNull(definitions.definitionFor(reference.getContextType()));
+ if (contextClass != null) {
+ reference.accept(
+ clazz -> classConsumer.accept(contextClass, joiner.asClassJoiner()),
+ fieldReference -> {
+ ProgramField field = contextClass.lookupProgramField(fieldReference);
+ if (field != null) {
+ fieldConsumer.accept(field, joiner.asFieldJoiner());
+ }
+ },
+ methodReference -> {
+ ProgramMethod method = contextClass.lookupProgramMethod(methodReference);
+ if (method != null) {
+ methodConsumer.accept(method, joiner.asMethodJoiner());
+ }
+ });
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T extends DexReference> void forEachThatMatches(
+ BiPredicate<DexReference, Joiner<?, ?, ?>> predicate,
+ BiConsumer<T, KeepInfo.Joiner<?, ?, ?>> consumer) {
+ minimumKeepInfo.forEach(
+ (reference, minimumKeepInfoForReference) -> {
+ if (predicate.test(reference, minimumKeepInfoForReference)) {
+ consumer.accept((T) reference, minimumKeepInfoForReference);
+ }
+ });
+ }
+
+ public KeepInfo.Joiner<?, ?, ?> getOrCreateMinimumKeepInfoFor(DexReference reference) {
+ return minimumKeepInfo.computeIfAbsent(
+ reference, ignoreKey(() -> KeepInfo.newEmptyJoinerFor(reference)));
+ }
+
+ public boolean hasMinimumKeepInfoThatMatches(
+ DexReference reference, Predicate<KeepInfo.Joiner<?, ?, ?>> predicate) {
+ KeepInfo.Joiner<?, ?, ?> minimumKeepInfoForReference = minimumKeepInfo.get(reference);
+ return minimumKeepInfoForReference != null && predicate.test(minimumKeepInfoForReference);
+ }
+
+ public boolean isEmpty() {
+ return minimumKeepInfo.isEmpty();
+ }
+
+ public void merge(MinimumKeepInfoCollection otherMinimumKeepInfo) {
+ otherMinimumKeepInfo.forEach(this::mergeMinimumKeepInfoFor);
+ }
+
+ public void mergeMinimumKeepInfoFor(
+ DexReference reference, KeepInfo.Joiner<?, ?, ?> minimumKeepInfoForReference) {
+ getOrCreateMinimumKeepInfoFor(reference).mergeUnsafe(minimumKeepInfoForReference);
+ }
+
+ public void pruneDeadItems(DexDefinitionSupplier definitions, Enqueuer enqueuer) {
+ MapUtils.removeIf(
+ minimumKeepInfo,
+ (reference, minimumKeepInfoForReference) -> {
+ assert !minimumKeepInfoForReference.isBottom();
+ ProgramDefinition definition =
+ reference.apply(
+ clazz -> asProgramClassOrNull(definitions.definitionFor(clazz)),
+ field ->
+ field.lookupOnProgramClass(
+ asProgramClassOrNull(definitions.definitionFor(field.getHolderType()))),
+ method ->
+ method.lookupOnProgramClass(
+ asProgramClassOrNull(definitions.definitionFor(method.getHolderType()))));
+ return definition == null || !enqueuer.isReachable(definition);
+ });
+ }
+
+ public KeepClassInfo.Joiner remove(DexType clazz) {
+ return (KeepClassInfo.Joiner) minimumKeepInfo.remove(clazz);
+ }
+
+ public KeepFieldInfo.Joiner remove(DexField field) {
+ return (KeepFieldInfo.Joiner) minimumKeepInfo.remove(field);
+ }
+
+ public KeepMethodInfo.Joiner remove(DexMethod method) {
+ return (KeepMethodInfo.Joiner) minimumKeepInfo.remove(method);
+ }
+
+ public MinimumKeepInfoCollection rewrittenWithLens(GraphLens graphLens) {
+ MinimumKeepInfoCollection rewrittenMinimumKeepInfo = new MinimumKeepInfoCollection();
+ forEach(
+ (reference, minimumKeepInfoForReference) -> {
+ DexReference rewrittenReference =
+ reference.apply(
+ type -> {
+ DexType rewrittenType = graphLens.lookupType(type);
+ if (rewrittenType.isPrimitiveType()) {
+ // May happen due to enum unboxing.
+ assert type.isClassType();
+ assert rewrittenType.isIntType();
+ return null;
+ }
+ return rewrittenType;
+ },
+ graphLens::getRenamedFieldSignature,
+ graphLens::getRenamedMethodSignature);
+ if (rewrittenReference != null) {
+ rewrittenMinimumKeepInfo
+ .getOrCreateMinimumKeepInfoFor(rewrittenReference)
+ .mergeUnsafe(minimumKeepInfoForReference);
+ }
+ });
+ return rewrittenMinimumKeepInfo;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index 948caf2..c585b2d 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -124,6 +124,10 @@
return this;
}
+ boolean isAccessModificationEnabled() {
+ return allowAccessModification;
+ }
+
boolean isObfuscating() {
return obfuscating;
}
@@ -380,14 +384,15 @@
}
// If either of the flags -dontshrink or -dontobfuscate, or shrinking or minification is
// turned off through the API, then add a match all rule which will apply that.
- if (!isShrinking() || !isObfuscating()) {
+ if (!isObfuscating() || !isOptimizing() || !isShrinking()) {
ProguardKeepRule rule =
ProguardKeepRule.defaultKeepAllRule(
modifiers -> {
- modifiers.setAllowsShrinking(isShrinking());
- // TODO(b/189807246): This should be removed.
- modifiers.setAllowsOptimization(true);
- modifiers.setAllowsObfuscation(isObfuscating());
+ modifiers
+ .setAllowsAccessModification(isAccessModificationEnabled())
+ .setAllowsObfuscation(isObfuscating())
+ .setAllowsOptimization(isOptimizing())
+ .setAllowsShrinking(isShrinking());
// In non-compatibility mode, adding -dontoptimize does not cause all annotations
// to be retained.
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
index 3021c88..7535a3e 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
@@ -25,12 +25,14 @@
return this;
}
- public void setAllowsShrinking(boolean allowsShrinking) {
+ public Builder setAllowsShrinking(boolean allowsShrinking) {
this.allowsShrinking = allowsShrinking;
+ return this;
}
- public void setAllowsOptimization(boolean allowsOptimization) {
+ public Builder setAllowsOptimization(boolean allowsOptimization) {
this.allowsOptimization = allowsOptimization;
+ return this;
}
public Builder setAllowsObfuscation(boolean allowsObfuscation) {
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 03e9de3..73f5c94 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -5,7 +5,6 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.utils.LensUtils.rewriteAndApplyIfNotPrimitiveType;
-import static com.android.tools.r8.utils.MapUtils.ignoreKey;
import static java.util.Collections.emptyMap;
import com.android.tools.r8.dex.Constants;
@@ -36,12 +35,11 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramField;
-import com.android.tools.r8.graph.ProgramMember;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteBuilderShrinker;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
@@ -53,16 +51,13 @@
import com.android.tools.r8.shaking.EnqueuerEvent.InstantiatedClassEnqueuerEvent;
import com.android.tools.r8.shaking.EnqueuerEvent.LiveClassEnqueuerEvent;
import com.android.tools.r8.shaking.EnqueuerEvent.UnconditionalKeepInfoEvent;
-import com.android.tools.r8.shaking.KeepInfo.Joiner;
import com.android.tools.r8.utils.ArrayUtils;
-import com.android.tools.r8.utils.Consumer3;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.OriginWithPosition;
import com.android.tools.r8.utils.PredicateSet;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.TriConsumer;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -91,41 +86,20 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
-import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class RootSetUtils {
- static void modifyDependentMinimumKeepInfo(
- Map<EnqueuerEvent, Map<DexReference, KeepInfo.Joiner<?, ?, ?>>> dependentMinimumKeepInfo,
- EnqueuerEvent event,
- ProgramDefinition dependent,
- Consumer<Joiner<?, ?, ?>> consumer) {
- Joiner<?, ?, ?> joiner =
- dependentMinimumKeepInfo
- .computeIfAbsent(event, ignoreKey(IdentityHashMap::new))
- .computeIfAbsent(
- dependent.getReference(),
- key ->
- key.apply(
- clazz -> KeepClassInfo.newEmptyJoiner(),
- field -> KeepFieldInfo.newEmptyJoiner(),
- method -> KeepMethodInfo.newEmptyJoiner()));
- consumer.accept(joiner);
- assert !joiner.isBottom();
- }
-
public static class RootSetBuilder {
private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final SubtypingInfo subtypingInfo;
private final DirectMappedDexApplication application;
private final Iterable<? extends ProguardConfigurationRule> rules;
- private final MutableItemsWithRules noShrinking = new MutableItemsWithRules();
- private final Map<EnqueuerEvent, Map<DexReference, KeepInfo.Joiner<?, ?, ?>>>
- dependentMinimumKeepInfo = new HashMap<>();
+ private final DependentMinimumKeepInfoCollection dependentMinimumKeepInfo =
+ new DependentMinimumKeepInfoCollection();
private final LinkedHashMap<DexReference, DexReference> reasonAsked = new LinkedHashMap<>();
private final LinkedHashMap<DexReference, DexReference> checkDiscarded = new LinkedHashMap<>();
private final Set<DexMethod> alwaysInline = Sets.newIdentityHashSet();
@@ -144,8 +118,6 @@
private final Set<DexType> noVerticalClassMerging = Sets.newIdentityHashSet();
private final Set<DexType> noHorizontalClassMerging = Sets.newIdentityHashSet();
private final Set<DexReference> neverPropagateValue = Sets.newIdentityHashSet();
- private final Map<DexReference, MutableItemsWithRules> dependentNoShrinking =
- new IdentityHashMap<>();
private final Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule =
new IdentityHashMap<>();
private final Map<DexReference, ProguardMemberRule> mayHaveSideEffects =
@@ -386,7 +358,6 @@
&& Sets.intersection(neverInline, forceInline).isEmpty()
: "A method cannot be marked as both -neverinline and -forceinline/-alwaysinline.";
return new RootSet(
- noShrinking,
dependentMinimumKeepInfo,
ImmutableList.copyOf(reasonAsked.values()),
ImmutableList.copyOf(checkDiscarded.values()),
@@ -409,7 +380,6 @@
mayHaveSideEffects,
noSideEffects,
assumedValues,
- dependentNoShrinking,
dependentKeepClassCompatRule,
identifierNameStrings,
ifRules,
@@ -481,9 +451,7 @@
neverInline,
neverInlineDueToSingleCaller,
neverClassInline,
- noShrinking,
dependentMinimumKeepInfo,
- dependentNoShrinking,
dependentKeepClassCompatRule,
Lists.newArrayList(delayedRootSetActionItems));
}
@@ -769,6 +737,7 @@
// TODO(b/67934426): Test this code.
public static void writeSeeds(
AppInfoWithLiveness appInfo, PrintStream out, Predicate<DexType> include) {
+ InternalOptions options = appInfo.app().options;
appInfo
.getKeepInfo()
.forEachPinnedType(
@@ -776,7 +745,8 @@
if (include.test(type)) {
out.println(type.toSourceString());
}
- });
+ },
+ options);
appInfo
.getKeepInfo()
.forEachPinnedField(
@@ -789,7 +759,8 @@
+ " "
+ field.name.toSourceString());
}
- });
+ },
+ options);
appInfo
.getKeepInfo()
.forEachPinnedMethod(
@@ -826,7 +797,8 @@
out.print(param.toSourceString());
}
out.println(")");
- });
+ },
+ options);
out.close();
}
@@ -1105,12 +1077,8 @@
addItemToSets(clazz, rule, null, null, ifRule);
}
- // TODO(b/192636793): This needs to use the precondition.
private void includeDescriptor(
- ProgramDefinition item,
- DexType type,
- ProguardKeepRuleBase context,
- DexProgramClass precondition) {
+ DexType type, ProguardKeepRuleBase rule, EnqueuerEvent preconditionEvent) {
if (type.isVoidType()) {
return;
}
@@ -1126,31 +1094,33 @@
}
// Keep the type if the item is also kept.
- dependentNoShrinking
- .computeIfAbsent(item.getReference(), x -> new MutableItemsWithRules())
- .addClassWithRule(type, context);
+ ProguardKeepRuleModifiers modifiers = rule.getModifiers();
+ if (appView.options().isShrinking() && !modifiers.allowsShrinking) {
+ dependentMinimumKeepInfo
+ .getOrCreateMinimumKeepInfoFor(preconditionEvent, clazz.getReference())
+ .addRule(rule)
+ .disallowShrinking();
+ }
// Disable minification for the type.
- if (appView.options().isMinificationEnabled()) {
- modifyDependentMinimumKeepInfo(
- dependentMinimumKeepInfo,
- UnconditionalKeepInfoEvent.get(),
- clazz,
- Joiner::disallowMinification);
+ if (appView.options().isMinificationEnabled() && !modifiers.allowsObfuscation) {
+ dependentMinimumKeepInfo
+ .getOrCreateMinimumKeepInfoFor(preconditionEvent, clazz.getReference())
+ .disallowMinification();
}
}
private void includeDescriptorClasses(
- ProgramDefinition item, ProguardKeepRuleBase context, DexProgramClass precondition) {
+ ProgramDefinition item, ProguardKeepRuleBase rule, EnqueuerEvent preconditionEvent) {
if (item.isMethod()) {
ProgramMethod method = item.asProgramMethod();
- includeDescriptor(method, method.getReturnType(), context, precondition);
+ includeDescriptor(method.getReturnType(), rule, preconditionEvent);
for (DexType value : method.getParameters()) {
- includeDescriptor(method, value, context, precondition);
+ includeDescriptor(value, rule, preconditionEvent);
}
} else if (item.isField()) {
ProgramField field = item.asProgramField();
- includeDescriptor(field, field.getType(), context, precondition);
+ includeDescriptor(field.getType(), rule, preconditionEvent);
} else {
assert item.isClass();
}
@@ -1162,12 +1132,13 @@
ProguardMemberRule rule,
DexProgramClass precondition,
ProguardIfRule ifRule) {
- if (context instanceof ProguardKeepRule) {
+ if (context.isProguardKeepRule()) {
if (!item.isProgramDefinition()) {
// Keep rules do not apply to non-program items.
return;
}
- evaluateKeepRule(item.asProgramDefinition(), context, rule, precondition, ifRule);
+ evaluateKeepRule(
+ item.asProgramDefinition(), context.asProguardKeepRule(), rule, precondition, ifRule);
} else if (context instanceof ProguardAssumeMayHaveSideEffectsRule) {
mayHaveSideEffects.put(item.getReference(), rule);
context.markAsUsed();
@@ -1335,7 +1306,7 @@
private synchronized void evaluateKeepRule(
ProgramDefinition item,
- ProguardConfigurationRule context,
+ ProguardKeepRule context,
ProguardMemberRule rule,
DexProgramClass precondition,
ProguardIfRule ifRule) {
@@ -1384,10 +1355,10 @@
}
// The reason for keeping should link to the conditional rule as a whole, if present.
- ProguardKeepRuleBase keepRule = ifRule != null ? ifRule : (ProguardKeepRuleBase) context;
+ ProguardKeepRuleBase keepRule = ifRule != null ? ifRule : context;
// The modifiers are specified on the actual keep rule (ie, the consequent/context).
- ProguardKeepRuleModifiers modifiers = ((ProguardKeepRule) context).getModifiers();
+ ProguardKeepRuleModifiers modifiers = context.getModifiers();
if (modifiers.isBottom()) {
// This rule is a no-op.
return;
@@ -1406,56 +1377,61 @@
}
}
- // TODO(b/192636793): Remove the noShrinking and dependentNoShrinking collections. A
- // prerequisite for this is that the ProguardKeepRule instances are added to the KeepInfo,
- // since this is needed for the -whyareyoukeeping graph.
- if (!modifiers.allowsShrinking) {
- if (precondition != null) {
- dependentNoShrinking
- .computeIfAbsent(precondition.getReference(), x -> new MutableItemsWithRules())
- .addReferenceWithRule(item.getReference(), keepRule);
- } else {
- noShrinking.addReferenceWithRule(item.getReference(), keepRule);
- }
- context.markAsUsed();
- }
-
EnqueuerEvent preconditionEvent;
if (precondition != null) {
preconditionEvent =
item.getAccessFlags().isStatic()
+ || (item.isMethod() && item.asMethod().getDefinition().isInstanceInitializer())
? new LiveClassEnqueuerEvent(precondition)
: new InstantiatedClassEnqueuerEvent(precondition);
} else {
preconditionEvent = UnconditionalKeepInfoEvent.get();
}
+ if (appView.options().isAccessModificationEnabled() && !modifiers.allowsAccessModification) {
+ dependentMinimumKeepInfo
+ .getOrCreateMinimumKeepInfoFor(preconditionEvent, item.getReference())
+ .disallowAccessModification();
+ context.markAsUsed();
+ }
+
if (appView.options().isAnnotationRemovalEnabled() && !modifiers.allowsAnnotationRemoval) {
- modifyDependentMinimumKeepInfo(
- dependentMinimumKeepInfo, preconditionEvent, item, Joiner::disallowAnnotationRemoval);
+ dependentMinimumKeepInfo
+ .getOrCreateMinimumKeepInfoFor(preconditionEvent, item.getReference())
+ .disallowAnnotationRemoval();
context.markAsUsed();
}
if (appView.options().isMinificationEnabled() && !modifiers.allowsObfuscation) {
- modifyDependentMinimumKeepInfo(
- dependentMinimumKeepInfo, preconditionEvent, item, Joiner::disallowMinification);
+ dependentMinimumKeepInfo
+ .getOrCreateMinimumKeepInfoFor(preconditionEvent, item.getReference())
+ .disallowMinification();
context.markAsUsed();
}
if (appView.options().isOptimizationEnabled() && !modifiers.allowsOptimization) {
- modifyDependentMinimumKeepInfo(
- dependentMinimumKeepInfo, preconditionEvent, item, Joiner::disallowOptimization);
+ dependentMinimumKeepInfo
+ .getOrCreateMinimumKeepInfoFor(preconditionEvent, item.getReference())
+ .disallowOptimization();
context.markAsUsed();
}
- if (appView.options().isShrinking() && !modifiers.allowsShrinking) {
- modifyDependentMinimumKeepInfo(
- dependentMinimumKeepInfo, preconditionEvent, item, Joiner::disallowShrinking);
+ if ((appView.options().isShrinking() || isMainDexRootSetBuilder())
+ && !modifiers.allowsShrinking) {
+ KeepInfo.Joiner<?, ?, ?> minimumKeepInfoForItem =
+ dependentMinimumKeepInfo
+ .getOrCreateMinimumKeepInfoFor(preconditionEvent, item.getReference())
+ .addRule(keepRule)
+ .disallowShrinking();
context.markAsUsed();
+
+ if (item.getAccessFlags().isPackagePrivateOrProtected()) {
+ minimumKeepInfoForItem.requireAccessModificationForRepackaging();
+ }
}
if (modifiers.includeDescriptorClasses) {
- includeDescriptorClasses(item, keepRule, precondition);
+ includeDescriptorClasses(item, keepRule, preconditionEvent);
context.markAsUsed();
}
}
@@ -1507,9 +1483,7 @@
final Set<DexMethod> neverInline;
final Set<DexMethod> neverInlineDueToSingleCaller;
final Set<DexType> neverClassInline;
- final MutableItemsWithRules noShrinking;
- final Map<EnqueuerEvent, Map<DexReference, KeepInfo.Joiner<?, ?, ?>>> dependentMinimumKeepInfo;
- final Map<DexReference, MutableItemsWithRules> dependentNoShrinking;
+ private final DependentMinimumKeepInfoCollection dependentMinimumKeepInfo;
final Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule;
final List<DelayedRootSetActionItem> delayedRootSetActionItems;
@@ -1517,433 +1491,23 @@
Set<DexMethod> neverInline,
Set<DexMethod> neverInlineDueToSingleCaller,
Set<DexType> neverClassInline,
- MutableItemsWithRules noShrinking,
- Map<EnqueuerEvent, Map<DexReference, KeepInfo.Joiner<?, ?, ?>>> dependentMinimumKeepInfo,
- Map<DexReference, MutableItemsWithRules> dependentNoShrinking,
+ DependentMinimumKeepInfoCollection dependentMinimumKeepInfo,
Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
List<DelayedRootSetActionItem> delayedRootSetActionItems) {
this.neverInline = neverInline;
this.neverInlineDueToSingleCaller = neverInlineDueToSingleCaller;
this.neverClassInline = neverClassInline;
- this.noShrinking = noShrinking;
this.dependentMinimumKeepInfo = dependentMinimumKeepInfo;
- this.dependentNoShrinking = dependentNoShrinking;
this.dependentKeepClassCompatRule = dependentKeepClassCompatRule;
this.delayedRootSetActionItems = delayedRootSetActionItems;
}
- public void forEachClassWithDependentItems(
- DexDefinitionSupplier definitions, Consumer<DexProgramClass> consumer) {
- for (DexReference reference : dependentNoShrinking.keySet()) {
- if (reference.isDexType()) {
- DexType type = reference.asDexType();
- DexProgramClass clazz = asProgramClassOrNull(definitions.definitionFor(type));
- if (clazz != null) {
- consumer.accept(clazz);
- }
- }
- }
- }
-
- public void forEachMemberWithDependentItems(
- DexDefinitionSupplier definitions,
- BiConsumer<DexEncodedMember<?, ?>, ItemsWithRules> consumer) {
- dependentNoShrinking.forEach(
- (reference, dependentItems) -> {
- if (reference.isDexMember()) {
- DexMember<?, ?> member = reference.asDexMember();
- DexProgramClass holder =
- asProgramClassOrNull(definitions.definitionForHolder(member));
- if (holder != null) {
- DexEncodedMember<?, ?> definition = holder.lookupMember(member);
- if (definition != null) {
- consumer.accept(definition, dependentItems);
- }
- }
- }
- });
- }
-
- public void forEachDependentInstanceConstructor(
- DexProgramClass clazz,
- AppView<?> appView,
- BiConsumer<ProgramMethod, Set<ProguardKeepRuleBase>> fn) {
- getDependentItems(clazz)
- .forEachMethod(
- (reference, reasons) -> {
- DexProgramClass holder =
- asProgramClassOrNull(appView.definitionForHolder(reference));
- if (holder != null) {
- ProgramMethod method = holder.lookupProgramMethod(reference);
- if (method != null && method.getDefinition().isInstanceInitializer()) {
- fn.accept(method, reasons);
- }
- }
- });
- }
-
- public void forEachDependentMember(
- DexDefinition item,
- AppView<?> appView,
- Consumer3<DexDefinition, ProgramMember<?, ?>, Set<ProguardKeepRuleBase>> fn) {
- getDependentItems(item)
- .forEachMember(
- (reference, reasons) -> {
- DexProgramClass holder =
- asProgramClassOrNull(appView.definitionForHolder(reference));
- if (holder != null) {
- ProgramMember<?, ?> member = holder.lookupProgramMember(reference);
- if (member != null) {
- fn.accept(item, member, reasons);
- }
- }
- });
- }
-
- public void forEachDependentNonStaticMember(
- DexDefinition item,
- AppView<?> appView,
- Consumer3<DexDefinition, ProgramMember<?, ?>, Set<ProguardKeepRuleBase>> fn) {
- forEachDependentMember(
- item,
- appView,
- (precondition, member, reasons) -> {
- if (!member.getDefinition().isStatic()) {
- fn.accept(precondition, member, reasons);
- }
- });
- }
-
- public void forEachDependentStaticMember(
- DexDefinition item,
- AppView<?> appView,
- Consumer3<DexDefinition, ProgramMember<?, ?>, Set<ProguardKeepRuleBase>> fn) {
- forEachDependentMember(
- item,
- appView,
- (precondition, member, reasons) -> {
- if (member.getDefinition().isStatic()) {
- fn.accept(precondition, member, reasons);
- }
- });
- }
-
- ItemsWithRules getDependentItems(DexDefinition item) {
- ItemsWithRules found = dependentNoShrinking.get(item.getReference());
- return found != null ? found : ItemsWithRules.empty();
- }
-
Set<ProguardKeepRuleBase> getDependentKeepClassCompatRule(DexType type) {
return dependentKeepClassCompatRule.get(type);
}
- public void forEachMinimumKeepInfo(
- AppView<? extends AppInfoWithClassHierarchy> appView,
- TriConsumer<EnqueuerEvent, DexProgramClass, KeepClassInfo.Joiner> classConsumer,
- TriConsumer<EnqueuerEvent, ProgramField, KeepFieldInfo.Joiner> fieldConsumer,
- TriConsumer<EnqueuerEvent, ProgramMethod, KeepMethodInfo.Joiner> methodConsumer) {
- dependentMinimumKeepInfo.forEach(
- (precondition, minimumKeepInfoForDependents) ->
- internalForEachMinimumKeepInfo(
- appView,
- minimumKeepInfoForDependents,
- precondition,
- classConsumer,
- fieldConsumer,
- methodConsumer));
- }
-
- private static void internalForEachMinimumKeepInfo(
- AppView<? extends AppInfoWithClassHierarchy> appView,
- Map<DexReference, KeepInfo.Joiner<?, ?, ?>> minimumKeepInfo,
- EnqueuerEvent precondition,
- TriConsumer<EnqueuerEvent, DexProgramClass, KeepClassInfo.Joiner> classConsumer,
- TriConsumer<EnqueuerEvent, ProgramField, KeepFieldInfo.Joiner> fieldConsumer,
- TriConsumer<EnqueuerEvent, ProgramMethod, KeepMethodInfo.Joiner> methodConsumer) {
- minimumKeepInfo.forEach(
- (reference, joiner) -> {
- DexProgramClass contextClass =
- asProgramClassOrNull(appView.definitionFor(reference.getContextType()));
- if (contextClass != null) {
- reference.accept(
- clazz -> classConsumer.accept(precondition, contextClass, joiner.asClassJoiner()),
- fieldReference -> {
- ProgramField field = contextClass.lookupProgramField(fieldReference);
- if (field != null) {
- fieldConsumer.accept(precondition, field, joiner.asFieldJoiner());
- }
- },
- methodReference -> {
- ProgramMethod method = contextClass.lookupProgramMethod(methodReference);
- if (method != null) {
- methodConsumer.accept(precondition, method, joiner.asMethodJoiner());
- }
- });
- }
- });
- }
- }
-
- abstract static class ItemsWithRules {
-
- public static ItemsWithRules empty() {
- return MutableItemsWithRules.EMPTY;
- }
-
- public abstract boolean containsClass(DexType type);
-
- public abstract boolean containsField(DexField field);
-
- public abstract boolean containsMethod(DexMethod method);
-
- public final boolean containsReference(DexReference reference) {
- return reference.apply(this::containsClass, this::containsField, this::containsMethod);
- }
-
- public abstract void forEachClass(Consumer<? super DexType> consumer);
-
- public abstract void forEachClass(
- BiConsumer<? super DexType, Set<ProguardKeepRuleBase>> consumer);
-
- public abstract void forEachField(Consumer<? super DexField> consumer);
-
- public abstract void forEachField(
- BiConsumer<? super DexField, Set<ProguardKeepRuleBase>> consumer);
-
- public abstract void forEachMember(Consumer<? super DexMember<?, ?>> consumer);
-
- public abstract void forEachMember(
- BiConsumer<? super DexMember<?, ?>, Set<ProguardKeepRuleBase>> consumer);
-
- public abstract void forEachMethod(Consumer<? super DexMethod> consumer);
-
- public abstract void forEachMethod(
- BiConsumer<? super DexMethod, Set<ProguardKeepRuleBase>> consumer);
-
- public abstract Set<ProguardKeepRuleBase> getRulesForClass(DexType type);
-
- public abstract Set<ProguardKeepRuleBase> getRulesForField(DexField field);
-
- public abstract Set<ProguardKeepRuleBase> getRulesForMethod(DexMethod method);
-
- public final Set<ProguardKeepRuleBase> getRulesForReference(DexReference reference) {
- return reference.apply(
- this::getRulesForClass, this::getRulesForField, this::getRulesForMethod);
- }
- }
-
- static class MutableItemsWithRules extends ItemsWithRules {
-
- private static final ItemsWithRules EMPTY =
- new MutableItemsWithRules(emptyMap(), emptyMap(), emptyMap());
-
- final Map<DexType, Set<ProguardKeepRuleBase>> classesWithRules;
- final Map<DexField, Set<ProguardKeepRuleBase>> fieldsWithRules;
- final Map<DexMethod, Set<ProguardKeepRuleBase>> methodsWithRules;
-
- MutableItemsWithRules() {
- this(new IdentityHashMap<>(), new IdentityHashMap<>(), new IdentityHashMap<>());
- }
-
- private MutableItemsWithRules(
- Map<DexType, Set<ProguardKeepRuleBase>> classesWithRules,
- Map<DexField, Set<ProguardKeepRuleBase>> fieldsWithRules,
- Map<DexMethod, Set<ProguardKeepRuleBase>> methodsWithRules) {
- this.classesWithRules = classesWithRules;
- this.fieldsWithRules = fieldsWithRules;
- this.methodsWithRules = methodsWithRules;
- }
-
- public void addAll(ItemsWithRules items) {
- items.forEachClass(this::addClassWithRules);
- items.forEachField(this::addFieldWithRules);
- items.forEachMethod(this::addMethodWithRules);
- }
-
- public void addClassWithRule(DexType type, ProguardKeepRuleBase rule) {
- classesWithRules.computeIfAbsent(type, ignore -> new HashSet<>()).add(rule);
- }
-
- public void addClassWithRules(DexType type, Set<ProguardKeepRuleBase> rules) {
- classesWithRules.computeIfAbsent(type, ignore -> new HashSet<>()).addAll(rules);
- }
-
- public void addFieldWithRule(DexField field, ProguardKeepRuleBase rule) {
- fieldsWithRules.computeIfAbsent(field, ignore -> new HashSet<>()).add(rule);
- }
-
- public void addFieldWithRules(DexField field, Set<ProguardKeepRuleBase> rules) {
- fieldsWithRules.computeIfAbsent(field, ignore -> new HashSet<>()).addAll(rules);
- }
-
- public void addMethodWithRule(DexMethod method, ProguardKeepRuleBase rule) {
- methodsWithRules.computeIfAbsent(method, ignore -> new HashSet<>()).add(rule);
- }
-
- public void addMethodWithRules(DexMethod method, Set<ProguardKeepRuleBase> rules) {
- methodsWithRules.computeIfAbsent(method, ignore -> new HashSet<>()).addAll(rules);
- }
-
- public void addReferenceWithRule(DexReference reference, ProguardKeepRuleBase rule) {
- reference.accept(
- this::addClassWithRule, this::addFieldWithRule, this::addMethodWithRule, rule);
- }
-
- public void addReferenceWithRules(DexReference reference, Set<ProguardKeepRuleBase> rules) {
- reference.accept(
- this::addClassWithRules, this::addFieldWithRules, this::addMethodWithRules, rules);
- }
-
- @Override
- public boolean containsClass(DexType type) {
- return classesWithRules.containsKey(type);
- }
-
- @Override
- public boolean containsField(DexField field) {
- return fieldsWithRules.containsKey(field);
- }
-
- @Override
- public boolean containsMethod(DexMethod method) {
- return methodsWithRules.containsKey(method);
- }
-
- public void forEachReference(Consumer<DexReference> consumer) {
- forEachClass(consumer);
- forEachMember(consumer);
- }
-
- @Override
- public void forEachClass(Consumer<? super DexType> consumer) {
- classesWithRules.keySet().forEach(consumer);
- }
-
- @Override
- public void forEachClass(BiConsumer<? super DexType, Set<ProguardKeepRuleBase>> consumer) {
- classesWithRules.forEach(consumer);
- }
-
- @Override
- public void forEachField(Consumer<? super DexField> consumer) {
- fieldsWithRules.keySet().forEach(consumer);
- }
-
- @Override
- public void forEachField(BiConsumer<? super DexField, Set<ProguardKeepRuleBase>> consumer) {
- fieldsWithRules.forEach(consumer);
- }
-
- @Override
- public void forEachMember(Consumer<? super DexMember<?, ?>> consumer) {
- forEachField(consumer);
- forEachMethod(consumer);
- }
-
- @Override
- public void forEachMember(
- BiConsumer<? super DexMember<?, ?>, Set<ProguardKeepRuleBase>> consumer) {
- forEachField(consumer);
- forEachMethod(consumer);
- }
-
- @Override
- public void forEachMethod(Consumer<? super DexMethod> consumer) {
- methodsWithRules.keySet().forEach(consumer);
- }
-
- @Override
- public void forEachMethod(BiConsumer<? super DexMethod, Set<ProguardKeepRuleBase>> consumer) {
- methodsWithRules.forEach(consumer);
- }
-
- @Override
- public Set<ProguardKeepRuleBase> getRulesForClass(DexType type) {
- return classesWithRules.get(type);
- }
-
- @Override
- public Set<ProguardKeepRuleBase> getRulesForField(DexField field) {
- return fieldsWithRules.get(field);
- }
-
- @Override
- public Set<ProguardKeepRuleBase> getRulesForMethod(DexMethod method) {
- return methodsWithRules.get(method);
- }
-
- public void removeClass(DexType type) {
- classesWithRules.remove(type);
- }
-
- public void removeField(DexField field) {
- fieldsWithRules.remove(field);
- }
-
- public void removeMethod(DexMethod method) {
- methodsWithRules.remove(method);
- }
-
- public void removeReference(DexReference reference) {
- reference.accept(this::removeClass, this::removeField, this::removeMethod);
- }
-
- public void putAll(ItemsWithRules items) {
- items.forEachClass(this::putClassWithRules);
- items.forEachField(this::putFieldWithRules);
- items.forEachMethod(this::putMethodWithRules);
- }
-
- public void putClassWithRules(DexType type, Set<ProguardKeepRuleBase> rules) {
- classesWithRules.put(type, rules);
- }
-
- public void putFieldWithRules(DexField field, Set<ProguardKeepRuleBase> rules) {
- fieldsWithRules.put(field, rules);
- }
-
- public void putMethodWithRules(DexMethod method, Set<ProguardKeepRuleBase> rules) {
- methodsWithRules.put(method, rules);
- }
-
- public void putReferenceWithRules(DexReference reference, Set<ProguardKeepRuleBase> rules) {
- reference.accept(
- this::putClassWithRules, this::putFieldWithRules, this::putMethodWithRules, rules);
- }
-
- public int size() {
- return classesWithRules.size() + fieldsWithRules.size() + methodsWithRules.size();
- }
-
- public void forEachReference(
- BiConsumer<? super DexReference, Set<ProguardKeepRuleBase>> consumer) {
- forEachClass(consumer);
- forEachMember(consumer);
- }
-
- private MutableItemsWithRules prune(Set<DexType> prunedClasses) {
- MutableItemsWithRules prunedItemsWithRules = new MutableItemsWithRules();
- forEachReference(
- (reference, rules) -> {
- if (!prunedClasses.contains(reference.getContextType())) {
- prunedItemsWithRules.addReferenceWithRules(reference, rules);
- }
- });
- return prunedItemsWithRules;
- }
-
- private MutableItemsWithRules rewrittenWithLens(GraphLens graphLens) {
- if (graphLens.isIdentityLens()) {
- return this;
- }
- MutableItemsWithRules rewrittenItemsWithRules = new MutableItemsWithRules();
- forEachReference(
- (reference, rules) ->
- rewriteAndApplyIfNotPrimitiveType(
- graphLens,
- reference,
- rewritten -> rewrittenItemsWithRules.addReferenceWithRules(rewritten, rules)));
- return rewrittenItemsWithRules;
+ public DependentMinimumKeepInfoCollection getDependentMinimumKeepInfo() {
+ return dependentMinimumKeepInfo;
}
}
@@ -1971,8 +1535,7 @@
public final Set<ProguardIfRule> ifRules;
private RootSet(
- MutableItemsWithRules noShrinking,
- Map<EnqueuerEvent, Map<DexReference, KeepInfo.Joiner<?, ?, ?>>> dependentMinimumKeepInfo,
+ DependentMinimumKeepInfoCollection dependentMinimumKeepInfo,
ImmutableList<DexReference> reasonAsked,
ImmutableList<DexReference> checkDiscarded,
Set<DexMethod> alwaysInline,
@@ -1994,7 +1557,6 @@
Map<DexReference, ProguardMemberRule> mayHaveSideEffects,
Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects,
Map<DexMember<?, ?>, ProguardMemberRule> assumedValues,
- Map<DexReference, MutableItemsWithRules> dependentNoShrinking,
Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
Set<DexReference> identifierNameStrings,
Set<ProguardIfRule> ifRules,
@@ -2003,9 +1565,7 @@
neverInline,
neverInlineDueToSingleCaller,
neverClassInline,
- noShrinking,
dependentMinimumKeepInfo,
- dependentNoShrinking,
dependentKeepClassCompatRule,
delayedRootSetActionItems);
this.reasonAsked = reasonAsked;
@@ -2046,14 +1606,10 @@
}
}
- void addConsequentRootSet(ConsequentRootSet consequentRootSet, boolean addNoShrinking) {
+ void addConsequentRootSet(ConsequentRootSet consequentRootSet) {
neverInline.addAll(consequentRootSet.neverInline);
neverInlineDueToSingleCaller.addAll(consequentRootSet.neverInlineDueToSingleCaller);
neverClassInline.addAll(consequentRootSet.neverClassInline);
- if (addNoShrinking) {
- noShrinking.addAll(consequentRootSet.noShrinking);
- }
- addDependentItems(consequentRootSet.dependentNoShrinking, dependentNoShrinking);
consequentRootSet.dependentKeepClassCompatRule.forEach(
(type, rules) ->
dependentKeepClassCompatRule
@@ -2062,18 +1618,20 @@
delayedRootSetActionItems.addAll(consequentRootSet.delayedRootSetActionItems);
}
- // Add dependent items that depend on -if rules.
- static void addDependentItems(
- Map<DexReference, ? extends ItemsWithRules> dependentItemsToAdd,
- Map<DexReference, MutableItemsWithRules> dependentItemsToAddTo) {
- dependentItemsToAdd.forEach(
- (reference, dependence) ->
- dependentItemsToAddTo
- .computeIfAbsent(reference, x -> new MutableItemsWithRules())
- .putAll(dependence));
+ public boolean isShrinkingDisallowedUnconditionally(
+ ProgramDefinition definition, InternalOptions options) {
+ if (!options.isShrinking()) {
+ return true;
+ }
+ return getDependentMinimumKeepInfo()
+ .getOrDefault(UnconditionalKeepInfoEvent.get(), MinimumKeepInfoCollection.empty())
+ .hasMinimumKeepInfoThatMatches(
+ definition.getReference(),
+ minimumKeepInfoForDefinition -> !minimumKeepInfoForDefinition.isShrinkingAllowed());
}
public void pruneDeadItems(DexDefinitionSupplier definitions, Enqueuer enqueuer) {
+ getDependentMinimumKeepInfo().pruneDeadItems(definitions, enqueuer);
pruneDeadReferences(noUnusedInterfaceRemoval, definitions, enqueuer);
pruneDeadReferences(noVerticalClassMerging, definitions, enqueuer);
pruneDeadReferences(noHorizontalClassMerging, definitions, enqueuer);
@@ -2087,94 +1645,96 @@
Enqueuer enqueuer) {
references.removeIf(
reference -> {
- if (reference.isDexType()) {
- DexClass definition = definitions.definitionFor(reference.asDexType());
- return definition == null || !enqueuer.isTypeLive(definition);
- }
-
- assert reference.isDexMember();
-
- DexMember<?, ?> member = reference.asDexMember();
- DexClass holder = definitions.definitionForHolder(member);
- DexEncodedMember<?, ?> definition = member.lookupOnClass(holder);
- if (definition == null) {
- return true;
- }
- if (holder.isProgramClass()) {
- if (definition.isDexEncodedField()) {
- DexEncodedField field = definition.asDexEncodedField();
- return !enqueuer.isFieldReferenced(field);
- }
- assert definition.isDexEncodedMethod();
- DexEncodedMethod method = definition.asDexEncodedMethod();
- return !enqueuer.isMethodLive(method) && !enqueuer.isMethodTargeted(method);
- }
- return !enqueuer.isNonProgramTypeLive(holder);
+ Definition definition =
+ reference.apply(
+ definitions::definitionFor,
+ field ->
+ field.lookupMemberOnClass(definitions.definitionFor(field.getHolderType())),
+ method ->
+ method.lookupMemberOnClass(
+ definitions.definitionFor(method.getHolderType())));
+ return definition == null || !enqueuer.isReachable(definition);
});
}
void shouldNotBeMinified(ProgramDefinition definition) {
- modifyDependentMinimumKeepInfo(
- dependentMinimumKeepInfo,
- UnconditionalKeepInfoEvent.get(),
- definition,
- Joiner::disallowMinification);
+ getDependentMinimumKeepInfo()
+ .getOrCreateUnconditionalMinimumKeepInfoFor(definition.getReference())
+ .disallowMinification();
}
- public boolean verifyKeptFieldsAreAccessedAndLive(AppInfoWithLiveness appInfo) {
- noShrinking.forEachField(
- reference -> {
- DexClass holder = appInfo.definitionForHolder(reference);
- DexEncodedField field = reference.lookupOnClass(holder);
- if (field != null
- && (field.isStatic()
- || isKeptDirectlyOrIndirectly(field.getHolderType(), appInfo))) {
- assert appInfo.isFieldRead(field)
- : "Expected kept field `" + field.toSourceString() + "` to be read";
- assert appInfo.isFieldWritten(field)
- : "Expected kept field `" + field.toSourceString() + "` to be written";
- }
- });
+ public boolean verifyKeptFieldsAreAccessedAndLive(AppView<AppInfoWithLiveness> appView) {
+ getDependentMinimumKeepInfo()
+ .getUnconditionalMinimumKeepInfoOrDefault(MinimumKeepInfoCollection.empty())
+ .forEachThatMatches(
+ (reference, minimumKeepInfo) ->
+ reference.isDexField() && !minimumKeepInfo.isShrinkingAllowed(),
+ (reference, minimumKeepInfo) -> {
+ DexField fieldReference = reference.asDexField();
+ DexProgramClass holder =
+ asProgramClassOrNull(appView.definitionForHolder(fieldReference));
+ ProgramField field = fieldReference.lookupOnProgramClass(holder);
+ if (field != null
+ && (field.getAccessFlags().isStatic()
+ || isKeptDirectlyOrIndirectly(field.getHolderType(), appView))) {
+ assert appView.appInfo().isFieldRead(field.getDefinition())
+ : "Expected kept field `" + fieldReference.toSourceString() + "` to be read";
+ assert appView.appInfo().isFieldWritten(field.getDefinition())
+ : "Expected kept field `"
+ + fieldReference.toSourceString()
+ + "` to be written";
+ }
+ });
return true;
}
- public boolean verifyKeptMethodsAreTargetedAndLive(AppInfoWithLiveness appInfo) {
- noShrinking.forEachMethod(
- reference -> {
- assert appInfo.isTargetedMethod(reference)
- : "Expected kept method `" + reference.toSourceString() + "` to be targeted";
- DexEncodedMethod method =
- appInfo.definitionForHolder(reference).lookupMethod(reference);
- if (!method.isAbstract()
- && isKeptDirectlyOrIndirectly(method.getHolderType(), appInfo)) {
- assert appInfo.isLiveMethod(reference)
- : "Expected non-abstract kept method `"
- + reference.toSourceString()
- + "` to be live";
- }
- });
+ public boolean verifyKeptMethodsAreTargetedAndLive(AppView<AppInfoWithLiveness> appView) {
+ getDependentMinimumKeepInfo()
+ .getUnconditionalMinimumKeepInfoOrDefault(MinimumKeepInfoCollection.empty())
+ .forEachThatMatches(
+ (reference, minimumKeepInfo) ->
+ reference.isDexMethod() && !minimumKeepInfo.isShrinkingAllowed(),
+ (reference, minimumKeepInfo) -> {
+ DexMethod methodReference = reference.asDexMethod();
+ assert appView.appInfo().isTargetedMethod(methodReference)
+ : "Expected kept method `" + reference.toSourceString() + "` to be targeted";
+ DexEncodedMethod method =
+ appView.definitionForHolder(methodReference).lookupMethod(methodReference);
+ if (!method.isAbstract()
+ && isKeptDirectlyOrIndirectly(methodReference.getHolderType(), appView)) {
+ assert appView.appInfo().isLiveMethod(methodReference)
+ : "Expected non-abstract kept method `"
+ + reference.toSourceString()
+ + "` to be live";
+ }
+ });
return true;
}
- public boolean verifyKeptTypesAreLive(AppInfoWithLiveness appInfo) {
- noShrinking.forEachClass(
- type -> {
- assert appInfo.isLiveProgramType(type)
- : "Expected kept type `" + type.toSourceString() + "` to be live";
- });
+ public boolean verifyKeptTypesAreLive(AppView<AppInfoWithLiveness> appView) {
+ getDependentMinimumKeepInfo()
+ .getUnconditionalMinimumKeepInfoOrDefault(MinimumKeepInfoCollection.empty())
+ .forEachThatMatches(
+ (reference, minimumKeepInfo) ->
+ reference.isDexType() && !minimumKeepInfo.isShrinkingAllowed(),
+ (reference, minimumKeepInfo) -> {
+ DexType type = reference.asDexType();
+ assert appView.appInfo().isLiveProgramType(type)
+ : "Expected kept type `" + type.toSourceString() + "` to be live";
+ });
return true;
}
- private boolean isKeptDirectlyOrIndirectly(DexType type, AppInfoWithLiveness appInfo) {
- if (noShrinking.containsClass(type)) {
- return true;
- }
- DexClass clazz = appInfo.definitionFor(type);
+ private boolean isKeptDirectlyOrIndirectly(DexType type, AppView<AppInfoWithLiveness> appView) {
+ DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(type));
if (clazz == null) {
return false;
}
+ if (isShrinkingDisallowedUnconditionally(clazz, appView.options())) {
+ return true;
+ }
if (clazz.superType != null) {
- return isKeptDirectlyOrIndirectly(clazz.superType, appInfo);
+ return isKeptDirectlyOrIndirectly(clazz.superType, appView);
}
return false;
}
@@ -2184,22 +1744,30 @@
GraphLens lens = appView.graphLens();
// Create a mapping from each required type to the set of required members on that type.
Map<DexType, Set<DexMember<?, ?>>> requiredMembersPerType = new IdentityHashMap<>();
- noShrinking.forEachClass(
- type -> {
- DexType rewrittenType = lens.lookupType(type);
- assert !appInfo.hasLiveness() || appInfo.withLiveness().isPinned(rewrittenType)
- : "Expected reference `" + rewrittenType.toSourceString() + "` to be pinned";
- requiredMembersPerType.computeIfAbsent(rewrittenType, key -> Sets.newIdentityHashSet());
- });
- noShrinking.forEachMember(
- member -> {
- DexMember<?, ?> rewrittenMember = lens.getRenamedMemberSignature(member);
- assert !appInfo.hasLiveness() || appInfo.withLiveness().isPinned(rewrittenMember)
- : "Expected reference `" + rewrittenMember.toSourceString() + "` to be pinned";
- requiredMembersPerType
- .computeIfAbsent(rewrittenMember.holder, key -> Sets.newIdentityHashSet())
- .add(rewrittenMember);
- });
+ getDependentMinimumKeepInfo()
+ .getUnconditionalMinimumKeepInfoOrDefault(MinimumKeepInfoCollection.empty())
+ .forEachThatMatches(
+ (reference, minimumKeepInfo) -> !minimumKeepInfo.isShrinkingAllowed(),
+ (reference, minimumKeepInfo) -> {
+ if (reference.isDexType()) {
+ DexType type = reference.asDexType();
+ DexType rewrittenType = lens.lookupType(type);
+ assert !appInfo.hasLiveness() || appInfo.withLiveness().isPinned(rewrittenType)
+ : "Expected reference `" + rewrittenType.toSourceString() + "` to be pinned";
+ requiredMembersPerType.computeIfAbsent(
+ rewrittenType, key -> Sets.newIdentityHashSet());
+ } else {
+ DexMember<?, ?> member = reference.asDexMember();
+ DexMember<?, ?> rewrittenMember = lens.getRenamedMemberSignature(member);
+ assert !appInfo.hasLiveness() || appInfo.withLiveness().isPinned(rewrittenMember)
+ : "Expected reference `"
+ + rewrittenMember.toSourceString()
+ + "` to be pinned";
+ requiredMembersPerType
+ .computeIfAbsent(rewrittenMember.holder, key -> Sets.newIdentityHashSet())
+ .add(rewrittenMember);
+ }
+ });
// Run through each class in the program and check that it has members it must have.
for (DexProgramClass clazz : appView.appInfo().classes()) {
@@ -2258,12 +1826,10 @@
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("RootSet");
- builder.append("\nnoShrinking: " + noShrinking.size());
builder.append("\nreasonAsked: " + reasonAsked.size());
builder.append("\ncheckDiscarded: " + checkDiscarded.size());
builder.append("\nnoSideEffects: " + noSideEffects.size());
builder.append("\nassumedValues: " + assumedValues.size());
- builder.append("\ndependentNoShrinking: " + dependentNoShrinking.size());
builder.append("\nidentifierNameStrings: " + identifierNameStrings.size());
builder.append("\nifRules: " + ifRules.size());
return builder.toString();
@@ -2312,18 +1878,14 @@
Set<DexMethod> neverInline,
Set<DexMethod> neverInlineDueToSingleCaller,
Set<DexType> neverClassInline,
- MutableItemsWithRules noShrinking,
- Map<EnqueuerEvent, Map<DexReference, KeepInfo.Joiner<?, ?, ?>>> dependentMinimumKeepInfo,
- Map<DexReference, MutableItemsWithRules> dependentNoShrinking,
+ DependentMinimumKeepInfoCollection dependentMinimumKeepInfo,
Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
List<DelayedRootSetActionItem> delayedRootSetActionItems) {
super(
neverInline,
neverInlineDueToSingleCaller,
neverClassInline,
- noShrinking,
dependentMinimumKeepInfo,
- dependentNoShrinking,
dependentKeepClassCompatRule,
delayedRootSetActionItems);
}
@@ -2355,10 +1917,9 @@
// Call the super builder to have if-tests calculated automatically.
RootSet rootSet = super.build(executorService);
return new MainDexRootSet(
- rootSet.noShrinking,
+ rootSet.getDependentMinimumKeepInfo(),
rootSet.reasonAsked,
rootSet.checkDiscarded,
- rootSet.dependentNoShrinking,
rootSet.ifRules,
rootSet.delayedRootSetActionItems);
}
@@ -2367,15 +1928,13 @@
public static class MainDexRootSet extends RootSet {
public MainDexRootSet(
- MutableItemsWithRules noShrinking,
+ DependentMinimumKeepInfoCollection dependentMinimumKeepInfo,
ImmutableList<DexReference> reasonAsked,
ImmutableList<DexReference> checkDiscarded,
- Map<DexReference, MutableItemsWithRules> dependentNoShrinking,
Set<ProguardIfRule> ifRules,
List<DelayedRootSetActionItem> delayedRootSetActionItems) {
super(
- noShrinking,
- emptyMap(),
+ dependentMinimumKeepInfo,
reasonAsked,
checkDiscarded,
Collections.emptySet(),
@@ -2397,26 +1956,12 @@
emptyMap(),
emptyMap(),
emptyMap(),
- dependentNoShrinking,
emptyMap(),
Collections.emptySet(),
ifRules,
delayedRootSetActionItems);
}
- @Override
- void addConsequentRootSet(ConsequentRootSet consequentRootSet, boolean addNoShrinking) {
- if (addNoShrinking) {
- noShrinking.addAll(consequentRootSet.noShrinking);
- }
- addDependentItems(consequentRootSet.dependentNoShrinking, dependentNoShrinking);
- consequentRootSet.dependentKeepClassCompatRule.forEach(
- (type, rules) ->
- dependentKeepClassCompatRule
- .computeIfAbsent(type, k -> new HashSet<>())
- .addAll(rules));
- }
-
public static MainDexRootSetBuilder builder(
AppView<? extends AppInfoWithClassHierarchy> appView,
SubtypingInfo subtypingInfo,
@@ -2433,21 +1978,6 @@
if (graphLens.isIdentityLens()) {
return this;
}
- Map<DexReference, MutableItemsWithRules> rewrittenDependent = new IdentityHashMap<>();
- dependentNoShrinking.forEach(
- (reference, rules) -> {
- // Rewriting a reference can result in us having to merge items with rules.
- rewriteAndApplyIfNotPrimitiveType(
- graphLens,
- reference,
- rewritten -> {
- MutableItemsWithRules rewrittenRules =
- rewrittenDependent.computeIfAbsent(
- graphLens.lookupReference(reference),
- rewrittenRef -> new MutableItemsWithRules());
- rewrittenRules.addAll(rules.rewrittenWithLens(graphLens));
- });
- });
ImmutableList.Builder<DexReference> rewrittenCheckDiscarded = ImmutableList.builder();
checkDiscarded.forEach(
@@ -2464,10 +1994,9 @@
// All delayed root set actions should have been processed at this point.
assert delayedRootSetActionItems.isEmpty();
return new MainDexRootSet(
- noShrinking.rewrittenWithLens(graphLens),
+ getDependentMinimumKeepInfo().rewrittenWithLens(graphLens),
rewrittenReasonAsked.build(),
rewrittenCheckDiscarded.build(),
- rewrittenDependent,
ifRules,
delayedRootSetActionItems);
}
@@ -2476,25 +2005,15 @@
if (prunedItems.isEmpty()) {
return this;
}
- Map<DexReference, MutableItemsWithRules> prunedDependent = new IdentityHashMap<>();
- dependentNoShrinking.forEach(
- (ref, rules) -> {
- if (prunedItems.getRemovedClasses().contains(ref.getContextType())) {
- // The dependent reference has been pruned and cannot lead to any additional items
- return;
- }
- prunedDependent.put(ref, rules.prune(prunedItems.getRemovedClasses()));
- });
// TODO(b/164019179): If rules can now reference dead items. These should be pruned or
- // rewritten
+ // rewritten.
ifRules.forEach(ProguardIfRule::canReferenceDeadTypes);
// All delayed root set actions should have been processed at this point.
assert delayedRootSetActionItems.isEmpty();
return new MainDexRootSet(
- noShrinking.prune(prunedItems.getRemovedClasses()),
+ getDependentMinimumKeepInfo(),
reasonAsked,
checkDiscarded,
- prunedDependent,
ifRules,
delayedRootSetActionItems);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 8bd2f01..0a49379 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -45,10 +45,10 @@
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
@@ -68,6 +68,7 @@
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.CollectionUtils;
import com.android.tools.r8.utils.FieldSignatureEquivalence;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.Timing;
@@ -213,6 +214,7 @@
private final DexApplication application;
private final AppInfoWithLiveness appInfo;
private final AppView<AppInfoWithLiveness> appView;
+ private final InternalOptions options;
private final SubtypingInfo subtypingInfo;
private final ExecutorService executorService;
private final MethodPoolCollection methodPoolCollection;
@@ -248,6 +250,7 @@
this.application = application;
this.appInfo = appView.appInfo();
this.appView = appView;
+ this.options = appView.options();
this.mainDexInfo = appInfo.getMainDexInfo();
this.subtypingInfo = appInfo.computeSubtypingInfo();
this.executorService = executorService;
@@ -291,9 +294,9 @@
// the return type and the parameter types of the method.
// TODO(b/156715504): Compute referenced-by-pinned in the keep info objects.
List<DexReference> pinnedItems = new ArrayList<>();
- appInfo.getKeepInfo().forEachPinnedType(pinnedItems::add);
- appInfo.getKeepInfo().forEachPinnedMethod(pinnedItems::add);
- appInfo.getKeepInfo().forEachPinnedField(pinnedItems::add);
+ appInfo.getKeepInfo().forEachPinnedType(pinnedItems::add, options);
+ appInfo.getKeepInfo().forEachPinnedMethod(pinnedItems::add, options);
+ appInfo.getKeepInfo().forEachPinnedField(pinnedItems::add, options);
extractPinnedItems(pinnedItems, AbortReason.PINNED_SOURCE);
for (DexProgramClass clazz : classes) {
@@ -728,7 +731,7 @@
// that `invoke-super A.method` instructions, which are in one of the methods from C, needs to
// be rewritten to `invoke-direct C.method$B`. This is valid even though A.method() is actually
// pinned, because this rewriting does not affect A.method() in any way.
- assert graphLens.assertPinnedNotModified(appInfo.getKeepInfo());
+ assert graphLens.assertPinnedNotModified(appInfo.getKeepInfo(), options);
for (DexProgramClass clazz : appInfo.classes()) {
for (DexEncodedMethod encodedMethod : clazz.methods()) {
@@ -1136,19 +1139,26 @@
interfaces.isEmpty()
? DexTypeList.empty()
: new DexTypeList(interfaces.toArray(DexType.EMPTY_ARRAY));
- // Step 2: replace fields and methods.
+ // Step 2: ensure -if rules cannot target the members that were merged into the target class.
+ directMethods.values().forEach(feedback::markMethodCannotBeKept);
+ virtualMethods.values().forEach(feedback::markMethodCannotBeKept);
+ for (int i = 0; i < source.instanceFields().size(); i++) {
+ feedback.markFieldCannotBeKept(mergedInstanceFields[i]);
+ }
+ for (int i = 0; i < source.staticFields().size(); i++) {
+ feedback.markFieldCannotBeKept(mergedStaticFields[i]);
+ }
+ // Step 3: replace fields and methods.
target.addDirectMethods(directMethods.values());
target.addVirtualMethods(virtualMethods.values());
target.setInstanceFields(mergedInstanceFields);
target.setStaticFields(mergedStaticFields);
- target.forEachField(feedback::markFieldCannotBeKept);
- target.forEachMethod(feedback::markMethodCannotBeKept);
- // Step 3: Clear the members of the source class since they have now been moved to the target.
+ // Step 4: Clear the members of the source class since they have now been moved to the target.
source.getMethodCollection().clearDirectMethods();
source.getMethodCollection().clearVirtualMethods();
source.clearInstanceFields();
source.clearStaticFields();
- // Step 4: Record merging.
+ // Step 5: Record merging.
mergedClasses.put(source.type, target.type);
assert !abortMerge;
assert GenericSignatureCorrectnessHelper.createForVerification(
@@ -1454,7 +1464,8 @@
// Returns the method that shadows the given method, or null if method is not shadowed.
private DexEncodedMethod findMethodInTarget(DexEncodedMethod method) {
- ResolutionResult resolutionResult = appInfo.resolveMethodOn(target, method.getReference());
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOn(target, method.getReference());
if (!resolutionResult.isSingleResolution()) {
// May happen in case of missing classes, or if multiple implementations were found.
abortMerge = true;
@@ -2018,7 +2029,7 @@
for (DexType type : method.proto.parameters.values) {
checkTypeReference(type);
}
- ResolutionResult resolutionResult =
+ MethodResolutionResult resolutionResult =
isInterface.isUnknown()
? appView.appInfo().unsafeResolveMethodDueToDexFormat(method)
: appView.appInfo().resolveMethod(method, isInterface.isTrue());
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 8c37bf1..53214f3 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -674,12 +674,17 @@
return true;
}
DexProgramClass holder = definition.getHolder().asProgramClass();
+ // TODO(b/192924387): Change such that the keep info for internal synthetics is always bottom.
+ if (!appView.getSyntheticItems().isSubjectToKeepRules(holder)) {
+ return false;
+ }
KeepInfoCollection keepInfo = appView.getKeepInfo();
- if (keepInfo.getClassInfo(holder).isPinned()) {
+ InternalOptions options = appView.options();
+ if (keepInfo.getClassInfo(holder).isPinned(options)) {
return true;
}
for (DexEncodedMember<?, ?> member : holder.members()) {
- if (keepInfo.getMemberInfo(member, holder).isPinned()) {
+ if (keepInfo.getMemberInfo(member, holder).isPinned(options)) {
return true;
}
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index d187792..eb97f48 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -669,6 +669,7 @@
return DexClassAndMethod.create(clazz, methodDefinition);
}
+ @SuppressWarnings("unchecked")
private <T extends DexClassAndMethod> DexEncodedMethod internalEnsureMethod(
DexMethod methodReference,
DexClass clazz,
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index 4c1ac7c..216fcfc 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -51,7 +51,9 @@
THROW_NSME("ThrowNSME", 16, true),
TWR_CLOSE_RESOURCE("TwrCloseResource", 17, true),
SERVICE_LOADER("ServiceLoad", 18, true),
- OUTLINE("Outline", 19, true);
+ OUTLINE("Outline", 19, true),
+ API_CONVERSION("APIConversion", 26, true),
+ API_CONVERSION_PARAMETERS("APIConversionParameters", 28, true);
static {
assert verifyNoOverlappingIds();
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 21bb4be..5827d53 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
@@ -27,9 +27,9 @@
import com.android.tools.r8.graph.GraphLens.FieldLookupResult;
import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
import com.android.tools.r8.graph.InitClassLens;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
@@ -348,7 +348,7 @@
return;
}
assert lookupResult.getType().isInterface() || lookupResult.getType().isVirtual();
- ResolutionResult resolutionResult =
+ MethodResolutionResult resolutionResult =
lookupResult.getType().isInterface()
? appInfo.resolveMethodOnInterface(method)
: appInfo.resolveMethodOnClass(method);
diff --git a/src/main/java/com/android/tools/r8/utils/Action.java b/src/main/java/com/android/tools/r8/utils/Action.java
index d69b130..5ab0177 100644
--- a/src/main/java/com/android/tools/r8/utils/Action.java
+++ b/src/main/java/com/android/tools/r8/utils/Action.java
@@ -6,5 +6,12 @@
@FunctionalInterface
public interface Action {
+
+ Action EMPTY = () -> {};
+
+ static Action empty() {
+ return EMPTY;
+ }
+
void execute();
}
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 0a9c18a..c0c5504 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -123,7 +123,7 @@
}
}
- public static final CfVersion SUPPORTED_CF_VERSION = CfVersion.V15;
+ public static final CfVersion SUPPORTED_CF_VERSION = CfVersion.V16_PREVIEW;
public static final CfVersion EXPERIMENTAL_CF_VERSION = CfVersion.V12;
public static final int SUPPORTED_DEX_VERSION =
@@ -609,8 +609,7 @@
@Override
public boolean isRepackagingEnabled() {
- return proguardConfiguration.getPackageObfuscationMode().isSome()
- && (isMinifying() || testing.repackageWithNoMinification);
+ return proguardConfiguration.getPackageObfuscationMode().isSome() && isMinifying();
}
@Override
@@ -1221,6 +1220,7 @@
// TODO(b/69963623): enable if everything is ready, including signature rewriting at call sites.
private boolean enableConstantPropagation = false;
+ private boolean enableExperimentalArgumentPropagation = false;
private boolean enableTypePropagation = true;
private void disableOptimization() {
@@ -1247,6 +1247,10 @@
return enableConstantPropagation || enableTypePropagation;
}
+ public boolean isExperimentalArgumentPropagationEnabled() {
+ return enableExperimentalArgumentPropagation;
+ }
+
public boolean isConstantPropagationEnabled() {
return enableConstantPropagation;
}
@@ -1254,6 +1258,16 @@
public boolean isTypePropagationEnabled() {
return enableTypePropagation;
}
+
+ public void setEnableConstantPropagation() {
+ assert !isConstantPropagationEnabled();
+ enableConstantPropagation = true;
+ }
+
+ public void setEnableExperimentalArgumentPropagation() {
+ assert !isExperimentalArgumentPropagationEnabled();
+ enableExperimentalArgumentPropagation = true;
+ }
}
public class HorizontalClassMergerOptions {
@@ -1403,6 +1417,22 @@
}
return TraversalContinuation.CONTINUE;
}
+
+ @Override
+ protected TraversalContinuation visitFields(
+ BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor,
+ ClassReference holder,
+ int minApi) {
+ return null;
+ }
+
+ @Override
+ protected TraversalContinuation visitMethods(
+ BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor,
+ ClassReference holder,
+ int minApi) {
+ return null;
+ }
});
});
}
@@ -1544,7 +1574,6 @@
// TODO(b/177333791): Set to true
public boolean checkForNotExpandingMainDexTracingResult = false;
public Set<String> allowedUnusedDontWarnPatterns = new HashSet<>();
- public boolean repackageWithNoMinification = false;
public boolean enableTestAssertions =
System.getProperty("com.android.tools.r8.enableTestAssertions") != null;
public boolean testEnableTestAssertions = false;
diff --git a/src/main/java/com/android/tools/r8/utils/IterableUtils.java b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
index 68a99e3..1c4aef5 100644
--- a/src/main/java/com/android/tools/r8/utils/IterableUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
@@ -62,6 +62,7 @@
return Collections.unmodifiableList(list);
}
+ @SuppressWarnings("unchecked")
public static <T, R extends T> R findOrDefault(
Iterable<T> iterable, Predicate<T> predicate, R defaultValue) {
for (T element : iterable) {
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 da825d1..ec1b7d0 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -60,6 +60,7 @@
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -143,17 +144,17 @@
if (parsedData == null) {
return baseRemapper.createRemappedPosition(position);
}
- Map.Entry<Integer, KotlinSourceDebugExtensionParser.Position> currentPosition =
- parsedData.lookup(line);
- if (currentPosition == null) {
+ Map.Entry<Integer, KotlinSourceDebugExtensionParser.Position> inlinedPosition =
+ parsedData.lookupInlinedPosition(line);
+ if (inlinedPosition == null) {
return baseRemapper.createRemappedPosition(position);
}
- int delta = line - currentPosition.getKey();
- int originalPosition = currentPosition.getValue().getRange().from + delta;
+ int inlineeLineDelta = line - inlinedPosition.getKey();
+ int originalInlineeLine = inlinedPosition.getValue().getRange().from + inlineeLineDelta;
try {
- String binaryName = currentPosition.getValue().getSource().getPath();
+ String binaryName = inlinedPosition.getValue().getSource().getPath();
String nameAndDescriptor =
- lineToMethodMapper.lookupNameAndDescriptor(binaryName, originalPosition);
+ lineToMethodMapper.lookupNameAndDescriptor(binaryName, originalInlineeLine);
if (nameAndDescriptor == null) {
return baseRemapper.createRemappedPosition(position);
}
@@ -173,8 +174,20 @@
factory.createString(returnTypeDescriptor),
argumentDexStringDescriptors);
if (!inlinee.equals(position.method)) {
+ // We have an inline from a different method than the current position.
+ Entry<Integer, KotlinSourceDebugExtensionParser.Position> calleePosition =
+ parsedData.lookupCalleePosition(line);
+ if (calleePosition != null) {
+ // Take the first line as the callee position
+ position =
+ new Position(
+ calleePosition.getValue().getRange().from,
+ position.file,
+ position.method,
+ position.callerPosition);
+ }
return baseRemapper.createRemappedPosition(
- new Position(originalPosition, null, inlinee, position));
+ new Position(originalInlineeLine, null, inlinee, position));
}
// This is the same position, so we should really not mark this as an inline position. Fall
// through to the default case.
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 91bfbe6..089154e 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -154,13 +154,13 @@
}
public static <T> ArrayList<T> newArrayList(T element) {
- ArrayList<T> list = new ArrayList<>();
+ ArrayList<T> list = new ArrayList<>(1);
list.add(element);
return list;
}
public static <T> ArrayList<T> newArrayList(T element, T other) {
- ArrayList<T> list = new ArrayList<>();
+ ArrayList<T> list = new ArrayList<>(2);
list.add(element);
list.add(other);
return list;
diff --git a/src/main/java/com/android/tools/r8/utils/MapUtils.java b/src/main/java/com/android/tools/r8/utils/MapUtils.java
index 2460a3c..87c0856 100644
--- a/src/main/java/com/android/tools/r8/utils/MapUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MapUtils.java
@@ -7,7 +7,7 @@
import com.android.tools.r8.utils.StringUtils.BraceType;
import java.util.IdentityHashMap;
import java.util.Map;
-import java.util.function.BiFunction;
+import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Supplier;
@@ -42,6 +42,10 @@
map.entrySet().removeIf(entry -> entry.getKey() == entry.getValue());
}
+ public static <K, V> void removeIf(Map<K, V> map, BiPredicate<K, V> predicate) {
+ map.entrySet().removeIf(entry -> predicate.test(entry.getKey(), entry.getValue()));
+ }
+
public static String toString(Map<?, ?> map) {
return StringUtils.join(
",", map.entrySet(), entry -> entry.getKey() + ":" + entry.getValue(), BraceType.TUBORG);
@@ -52,7 +56,7 @@
IntFunction<Map<K2, V2>> factory,
Function<K1, K2> keyMapping,
Function<V1, V2> valueMapping,
- BiFunction<V2, V2, V2> valueMerger) {
+ TriFunction<K2, V2, V2, V2> valueMerger) {
Map<K2, V2> result = factory.apply(map.size());
map.forEach(
(key, value) -> {
@@ -63,7 +67,7 @@
V2 newValue = valueMapping.apply(value);
V2 existingValue = result.put(newKey, newValue);
if (existingValue != null) {
- result.put(newKey, valueMerger.apply(existingValue, newValue));
+ result.put(newKey, valueMerger.apply(newKey, existingValue, newValue));
}
});
return result;
diff --git a/src/main/java/com/android/tools/r8/utils/SetUtils.java b/src/main/java/com/android/tools/r8/utils/SetUtils.java
index 01b58e7..bd97bd7 100644
--- a/src/main/java/com/android/tools/r8/utils/SetUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/SetUtils.java
@@ -7,6 +7,7 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.Collections;
+import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Set;
import java.util.function.Function;
@@ -22,6 +23,12 @@
return false;
}
+ public static <T> HashSet<T> newHashSet(T element) {
+ HashSet<T> result = new HashSet<>(1);
+ result.add(element);
+ return result;
+ }
+
public static <T> Set<T> newIdentityHashSet(T element) {
Set<T> result = Sets.newIdentityHashSet();
result.add(element);
@@ -85,6 +92,12 @@
return out;
}
+ public static <T> T removeFirst(Set<T> set) {
+ T element = set.iterator().next();
+ set.remove(element);
+ return element;
+ }
+
public static <T> Set<T> unionIdentityHashSet(Set<T> one, Set<T> other) {
Set<T> union = Sets.newIdentityHashSet();
union.addAll(one);
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureSet.java b/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureSet.java
index 8ef5506..5122253 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureSet.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureSet.java
@@ -94,6 +94,10 @@
return backing.contains(signature);
}
+ public boolean contains(DexClassAndMethod method) {
+ return contains(method.getMethodSignature());
+ }
+
@Override
public boolean containsAll(Collection<?> collection) {
return backing.containsAll(collection);
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldMap.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldMap.java
index 24f7e05..dc6a4be 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldMap.java
@@ -7,12 +7,15 @@
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.utils.ProgramFieldEquivalence;
import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.ImmutableMap;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
public class ProgramFieldMap<V> extends ProgramMemberMap<ProgramField, V> {
+ private static final ProgramFieldMap<?> EMPTY = new ProgramFieldMap<>(ImmutableMap::of);
+
private ProgramFieldMap(Supplier<Map<Wrapper<ProgramField>, V>> backingFactory) {
super(backingFactory);
}
@@ -21,6 +24,11 @@
return new ProgramFieldMap<>(HashMap::new);
}
+ @SuppressWarnings("unchecked")
+ public static <V> ProgramFieldMap<V> empty() {
+ return (ProgramFieldMap<V>) EMPTY;
+ }
+
@Override
Wrapper<ProgramField> wrap(ProgramField method) {
return ProgramFieldEquivalence.get().wrap(method);
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodMap.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodMap.java
index a3e1b6b..8fdf054 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodMap.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.utils.ProgramMethodEquivalence;
import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.ImmutableMap;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -14,6 +15,8 @@
public class ProgramMethodMap<V> extends ProgramMemberMap<ProgramMethod, V> {
+ private static final ProgramMethodMap<?> EMPTY = new ProgramMethodMap<>(ImmutableMap::of);
+
private ProgramMethodMap(Supplier<Map<Wrapper<ProgramMethod>, V>> backingFactory) {
super(backingFactory);
}
@@ -26,6 +29,11 @@
return new ProgramMethodMap<>(ConcurrentHashMap::new);
}
+ @SuppressWarnings("unchecked")
+ public static <V> ProgramMethodMap<V> empty() {
+ return (ProgramMethodMap<V>) EMPTY;
+ }
+
@Override
Wrapper<ProgramMethod> wrap(ProgramMethod method) {
return ProgramMethodEquivalence.get().wrap(method);
diff --git a/src/test/examplesJava16/pattern_matching_for_instanceof/Main.java b/src/test/examplesJava16/pattern_matching_for_instanceof/Main.java
new file mode 100644
index 0000000..35a3632
--- /dev/null
+++ b/src/test/examplesJava16/pattern_matching_for_instanceof/Main.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package pattern_matching_for_instanceof;
+
+final class Main {
+ public static void main(String[] args) {
+ Object obj = "Hello, world!";
+ if (obj instanceof String s) {
+ System.out.println(s);
+ }
+ }
+}
diff --git a/src/test/examplesJava15/records/EmptyRecord.java b/src/test/examplesJava16/records/EmptyRecord.java
similarity index 100%
rename from src/test/examplesJava15/records/EmptyRecord.java
rename to src/test/examplesJava16/records/EmptyRecord.java
diff --git a/src/test/examplesJava15/records/RecordInstanceOf.java b/src/test/examplesJava16/records/RecordInstanceOf.java
similarity index 100%
rename from src/test/examplesJava15/records/RecordInstanceOf.java
rename to src/test/examplesJava16/records/RecordInstanceOf.java
diff --git a/src/test/examplesJava15/records/RecordInvokeCustom.java b/src/test/examplesJava16/records/RecordInvokeCustom.java
similarity index 100%
rename from src/test/examplesJava15/records/RecordInvokeCustom.java
rename to src/test/examplesJava16/records/RecordInvokeCustom.java
diff --git a/src/test/examplesJava15/records/RecordReflection.java b/src/test/examplesJava16/records/RecordReflection.java
similarity index 100%
rename from src/test/examplesJava15/records/RecordReflection.java
rename to src/test/examplesJava16/records/RecordReflection.java
diff --git a/src/test/examplesJava15/records/RecordWithMembers.java b/src/test/examplesJava16/records/RecordWithMembers.java
similarity index 100%
rename from src/test/examplesJava15/records/RecordWithMembers.java
rename to src/test/examplesJava16/records/RecordWithMembers.java
diff --git a/src/test/examplesJava15/records/SimpleRecord.java b/src/test/examplesJava16/records/SimpleRecord.java
similarity index 100%
rename from src/test/examplesJava15/records/SimpleRecord.java
rename to src/test/examplesJava16/records/SimpleRecord.java
diff --git a/src/test/examplesJava15/sealed/Compiler.java b/src/test/examplesJava16/sealed/Compiler.java
similarity index 100%
rename from src/test/examplesJava15/sealed/Compiler.java
rename to src/test/examplesJava16/sealed/Compiler.java
diff --git a/src/test/examplesJava15/sealed/D8Compiler.java b/src/test/examplesJava16/sealed/D8Compiler.java
similarity index 100%
rename from src/test/examplesJava15/sealed/D8Compiler.java
rename to src/test/examplesJava16/sealed/D8Compiler.java
diff --git a/src/test/examplesJava15/sealed/Main.java b/src/test/examplesJava16/sealed/Main.java
similarity index 100%
rename from src/test/examplesJava15/sealed/Main.java
rename to src/test/examplesJava16/sealed/Main.java
diff --git a/src/test/examplesJava15/sealed/R8Compiler.java b/src/test/examplesJava16/sealed/R8Compiler.java
similarity index 100%
rename from src/test/examplesJava15/sealed/R8Compiler.java
rename to src/test/examplesJava16/sealed/R8Compiler.java
diff --git a/src/test/java/com/android/tools/r8/D8TestCompileResult.java b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
index 6d7634c..3908d64 100644
--- a/src/test/java/com/android/tools/r8/D8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
@@ -5,7 +5,10 @@
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.utils.AndroidApp;
+import java.io.IOException;
+import java.nio.file.Path;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
public class D8TestCompileResult extends TestCompileResult<D8TestCompileResult, D8TestRunResult> {
private final String proguardMap;
@@ -54,4 +57,13 @@
public D8TestRunResult createRunResult(TestRuntime runtime, ProcessResult result) {
return new D8TestRunResult(app, runtime, result, proguardMap);
}
+
+ public D8TestRunResult runWithJaCoCo(
+ Path output, TestRuntime runtime, String mainClass, String... args)
+ throws IOException, ExecutionException {
+ setSystemProperty("jacoco-agent.destfile", output.toString());
+ setSystemProperty("jacoco-agent.dumponexit", "true");
+ setSystemProperty("jacoco-agent.output", "file");
+ return run(runtime, mainClass, args);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/JvmTestBuilder.java b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
index 9951a04..f92a490 100644
--- a/src/test/java/com/android/tools/r8/JvmTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
@@ -158,6 +158,23 @@
return self();
}
+ public JvmTestBuilder enableJaCoCoAgentForOfflineInstrumentedCode(Path jacocoAgent, Path output) {
+ addProgramFiles(jacocoAgent);
+ addVmArguments(
+ "-Djacoco-agent.destfile=" + output.toString(),
+ "-Djacoco-agent.dumponexit=true",
+ "-Djacoco-agent.output=file");
+ return self();
+ }
+
+ public JvmTestBuilder enableJaCoCoAgent(Path jacocoAgent, Path output) {
+ addProgramFiles(jacocoAgent);
+ addVmArguments(
+ String.format(
+ "-javaagent:%s=destfile=%s,dumponexit=true,output=file", jacocoAgent, output));
+ return self();
+ }
+
public JvmTestBuilder addVmArguments(Collection<String> arguments) {
vmArguments.addAll(arguments);
return self();
diff --git a/src/test/java/com/android/tools/r8/R8CfVersionTest.java b/src/test/java/com/android/tools/r8/R8CfVersionTest.java
index 8e9b11c..fcc29c1 100644
--- a/src/test/java/com/android/tools/r8/R8CfVersionTest.java
+++ b/src/test/java/com/android/tools/r8/R8CfVersionTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -43,6 +44,8 @@
@Test
public void testCfVersionR8Lib() throws IOException {
+ // Only run when testing R8 lib as only then do we know it is built and up-to-date.
+ assumeTrue(ToolHelper.isTestingR8Lib());
CodeInspector inspector = new CodeInspector(ToolHelper.R8LIB_JAR);
inspector.forAllClasses(
clazz -> {
diff --git a/src/test/java/com/android/tools/r8/TestRuntime.java b/src/test/java/com/android/tools/r8/TestRuntime.java
index a51fcf0..6fd2f96 100644
--- a/src/test/java/com/android/tools/r8/TestRuntime.java
+++ b/src/test/java/com/android/tools/r8/TestRuntime.java
@@ -29,7 +29,7 @@
JDK9("jdk9", 53),
JDK10("jdk10", 54),
JDK11("jdk11", 55),
- JDK15("jdk15", 59),
+ JDK16("jdk16", 60),
;
private final String name;
@@ -70,13 +70,13 @@
private static final Path JDK9_PATH =
Paths.get(ToolHelper.THIRD_PARTY_DIR, "openjdk", "openjdk-9.0.4");
private static final Path JDK11_PATH = Paths.get(ToolHelper.THIRD_PARTY_DIR, "openjdk", "jdk-11");
- private static final Path JDK15_PATH = Paths.get(ToolHelper.THIRD_PARTY_DIR, "openjdk", "jdk-15");
+ private static final Path JDK16_PATH = Paths.get(ToolHelper.THIRD_PARTY_DIR, "openjdk", "jdk-16");
private static final Map<CfVm, Path> jdkPaths =
ImmutableMap.of(
CfVm.JDK8, JDK8_PATH,
CfVm.JDK9, JDK9_PATH,
CfVm.JDK11, JDK11_PATH,
- CfVm.JDK15, JDK15_PATH);
+ CfVm.JDK16, JDK16_PATH);
public static CfRuntime getCheckedInJdk(CfVm vm) {
if (vm == CfVm.JDK8) {
@@ -121,9 +121,9 @@
return new CfRuntime(CfVm.JDK11, getCheckedInJdkHome(CfVm.JDK11));
}
- // TODO(b/169692487): Add this to 'getCheckedInCfRuntimes' when we start having support for JDK15.
- public static CfRuntime getCheckedInJdk15() {
- return new CfRuntime(CfVm.JDK15, getCheckedInJdkHome(CfVm.JDK15));
+ // TODO(b/169692487): Add this to 'getCheckedInCfRuntimes' when we start having support for JDK16.
+ public static CfRuntime getCheckedInJdk16() {
+ return new CfRuntime(CfVm.JDK16, getCheckedInJdkHome(CfVm.JDK16));
}
public static List<CfRuntime> getCheckedInCfRuntimes() {
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index f0d2830..a1bdeb8 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -151,9 +151,9 @@
private static final String PROGUARD5_2_1 = "third_party/proguard/proguard5.2.1/bin/proguard";
private static final String PROGUARD6_0_1 = "third_party/proguard/proguard6.0.1/bin/proguard";
private static final String PROGUARD = PROGUARD5_2_1;
- public static final String JACOCO_AGENT =
- "third_party/jacoco/0.8.2/org.jacoco.agent-0.8.2-runtime.jar";
- public static final String JACOCO_CLI = "third_party/jacoco/0.8.6/lib/jacococli.jar";
+ public static final Path JACOCO_ROOT = Paths.get("third_party", "jacoco", "0.8.6");
+ public static final Path JACOCO_AGENT = JACOCO_ROOT.resolve(Paths.get("lib", "jacocoagent.jar"));
+ public static final Path JACOCO_CLI = JACOCO_ROOT.resolve(Paths.get("lib", "jacococli.jar"));
public static final String PROGUARD_SETTINGS_FOR_INTERNAL_APPS = "third_party/proguardsettings/";
private static final String RETRACE6_0_1 = "third_party/proguard/proguard6.0.1/bin/retrace";
@@ -1593,6 +1593,36 @@
return processResult;
}
+ public static ProcessResult runJaCoCoInstrument(Path sourceClassFiles, Path outputDirectory)
+ throws IOException {
+ List<String> cmdline = new ArrayList<>();
+ cmdline.add(TestRuntime.getSystemRuntime().asCf().getJavaExecutable().toString());
+ cmdline.add("-jar");
+ cmdline.add(ToolHelper.JACOCO_CLI.toString());
+ cmdline.add("instrument");
+ cmdline.add(sourceClassFiles.toString());
+ cmdline.add("--dest");
+ cmdline.add(outputDirectory.toString());
+ ProcessBuilder builder = new ProcessBuilder(cmdline);
+ return ToolHelper.runProcess(builder);
+ }
+
+ public static ProcessResult runJaCoCoReport(Path classfiles, Path jacocoExec, Path reportFile)
+ throws IOException {
+ List<String> cmdline = new ArrayList<>();
+ cmdline.add(TestRuntime.getSystemRuntime().asCf().getJavaExecutable().toString());
+ cmdline.add("-jar");
+ cmdline.add(ToolHelper.JACOCO_CLI.toString());
+ cmdline.add("report");
+ cmdline.add(jacocoExec.toString());
+ cmdline.add("--classfiles");
+ cmdline.add(classfiles.toString());
+ cmdline.add("--csv");
+ cmdline.add(reportFile.toString());
+ ProcessBuilder builder = new ProcessBuilder(cmdline);
+ return ToolHelper.runProcess(builder);
+ }
+
private static Path findNonConflictingDestinationFilePath(Path testOutputPath) {
int index = 0;
Path destFilePath;
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/NonPublicKeptClassPublicizerTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/NonPublicKeptClassPublicizerTest.java
new file mode 100644
index 0000000..bd4bf25
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/NonPublicKeptClassPublicizerTest.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.accessrelaxation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPublic;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import java.lang.reflect.Modifier;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class NonPublicKeptClassPublicizerTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection parameters() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .allowAccessModification()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+ assertThat(mainClassSubject, not(isPublic()));
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("false");
+ }
+
+ static class Main {
+ public static void main(String[] args) {
+ System.out.println(Modifier.isPublic(Main.class.getModifiers()));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java
index dfaecd1..8c139fe 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java
@@ -13,6 +13,7 @@
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.F_SAME1;
import static org.objectweb.asm.Opcodes.IFEQ;
+import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
@@ -27,7 +28,9 @@
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
import com.android.tools.r8.transformers.MethodTransformer;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.IntBox;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
@@ -89,10 +92,11 @@
.collect(Collectors.toList());
for (ParsedApiClass apiClass : apiClasses) {
+ String apiClassDescriptor = getApiClassReference(apiClass).getDescriptor();
consumer.accept(
- getApiClassDescriptor(apiClass),
+ apiClassDescriptor,
transformer(AndroidApiDatabaseClassTemplate.class)
- .setClassDescriptor(getApiClassDescriptor(apiClass))
+ .setClassDescriptor(apiClassDescriptor)
.addMethodTransformer(getInitTransformer(apiClass))
.addMethodTransformer(getApiLevelTransformer(apiClass))
.addMethodTransformer(getGetMemberCountTransformer(apiClass))
@@ -103,6 +107,8 @@
.removeMethods(MethodPredicate.onName("placeHolderForGetMemberCount"))
.removeMethods(MethodPredicate.onName("placeHolderForVisitFields"))
.removeMethods(MethodPredicate.onName("placeHolderForVisitMethods"))
+ .setSourceFile(
+ DescriptorUtils.getSimpleClassNameFromDescriptor(apiClassDescriptor) + ".java")
.computeMaxs()
.transform(ClassWriter.COMPUTE_MAXS));
}
@@ -137,13 +143,17 @@
.replace("Template", "ForPackage_" + pkg.replace(".", "_")));
}
- private static String getApiClassDescriptor(ParsedApiClass apiClass) {
- return DescriptorUtils.javaTypeToDescriptor(
- AndroidApiDatabaseClassTemplate.class
- .getTypeName()
- .replace(
- "Template",
- "ForClass_" + apiClass.getClassReference().getTypeName().replace(".", "_")));
+ private static ClassReference getApiClassReference(ParsedApiClass apiClass) {
+ return fromTemplate(apiClass.getClassReference());
+ }
+
+ private static ClassReference fromTemplate(ClassReference classReference) {
+ String descriptor =
+ DescriptorUtils.javaTypeToDescriptor(
+ AndroidApiDatabaseClassTemplate.class
+ .getTypeName()
+ .replace("Template", "ForClass_" + classReference.getTypeName().replace(".", "_")));
+ return Reference.classFromDescriptor(descriptor);
}
// The transformer below changes AndroidApiDatabaseClassTemplate.<init> from:
@@ -190,17 +200,24 @@
// placeHolder();
// return TraversalContinuation.CONTINUE;
// into
- // TraversalContinuation s1 = visitField("field1", "descriptor1", apiLevel1, visitor)
+ // TraversalContinuation s1 = visitField(visitor, holder, minApiClass, apiLevel1, "field1",
+ // "descriptor1")
// if (s1.shouldBreak()) {
// return s1;
// }
- // TraversalContinuation s2 = visitField("field2", "descriptor2", apiLevel2, visitor)
+ // TraversalContinuation s2 = visitField(visitor, holder, minApiClass, apiLevel2, "field2",
+ // // "descriptor2")
// if (s2.shouldBreak()) {
// return s2;
// }
// ...
+ // AndroidApiClassForClass_class_name1() super1 = new AndroidApiClassForClass_class_name1();
+ // TraversalContinuation sN = super1.visitFields(
+ // visitor, holder, max(minApiClass, minApiForLink));
+ // ...
// return TraversalContinuation.CONTINUE;
private static MethodTransformer getVisitFieldsTransformer(ParsedApiClass apiClass) {
+ IntBox lineNumberBox = new IntBox(0);
return replaceCode(
"placeHolderForVisitFields",
transformer -> {
@@ -208,19 +225,26 @@
(apiLevel, references) -> {
references.forEach(
reference -> {
+ Label labelStart = new Label();
+ transformer.visitLabel(labelStart);
+ transformer.visitLineNumber(lineNumberBox.getAndIncrement(), labelStart);
transformer.visitVarInsn(ALOAD, 0);
+ transformer.visitVarInsn(ALOAD, 1);
+ transformer.visitVarInsn(ALOAD, 2);
+ transformer.visitVarInsn(ILOAD, 3);
+ transformer.visitLdcInsn(apiLevel.getLevel());
transformer.visitLdcInsn(reference.getFieldName());
transformer.visitLdcInsn(reference.getFieldType().getDescriptor());
- transformer.visitLdcInsn(apiLevel.getLevel());
- transformer.visitVarInsn(ALOAD, 1);
transformer.visitMethodInsn(
INVOKEVIRTUAL,
ANDROID_API_CLASS.getBinaryName(),
"visitField",
- "(Ljava/lang/String;"
- + "Ljava/lang/String;"
+ "(Ljava/util/function/BiFunction;"
+ + descriptor(ClassReference.class)
+ "I"
- + "Ljava/util/function/BiFunction;)"
+ + "I"
+ + "Ljava/lang/String;"
+ + "Ljava/lang/String;)"
+ TRAVERSAL_CONTINUATION.getDescriptor(),
false);
// Note that instead of storing the result here, we dup it on the stack.
@@ -246,6 +270,19 @@
transformer.visitInsn(POP);
});
});
+ if (!apiClass.isInterface()) {
+ apiClass.visitSuperType(
+ (superType, apiLevel) -> {
+ addMembersForParent(
+ transformer,
+ superType,
+ "visitFields",
+ apiLevel,
+ lineNumberBox.getAndIncrement());
+ });
+ }
+ // No need to visit fields on interfaces since they have to be static and should not be
+ // called on a super instance.
});
}
@@ -253,19 +290,24 @@
// placeHolderForVisitMethods();
// return TraversalContinuation.CONTINUE;
// into
- // TraversalContinuation s1 = visitMethod(
- // "method1", new String[] { "param11", ... , "param1X" }, null/return1, apiLevel1, visitor)
+ // TraversalContinuation s1 = visitMethod(visitor, holder, minApiClass, apiLevel1,
+ // "method1", new String[] { "param11", ... , "param1X" }, null/return1)
// if (s1.shouldBreak()) {
// return s1;
// }
- // TraversalContinuation s1 = visitMethod(
- // "method2", new String[] { "param21", ... , "param2X" }, null/return2, apiLevel2, visitor)
+ // TraversalContinuation s1 = visitMethod(visitor, holder, minApiClass, apiLevel2,
+ // "method2", new String[] { "param21", ... , "param2X" }, null/return2)
// if (s2.shouldBreak()) {
// return s2;
// }
// ...
+ // AndroidApiClassForClass_class_name1() super1 = new AndroidApiClassForClass_class_name1();
+ // TraversalContinuation sN = super1.visitMethods(
+ // visitor, holder, max(minApiClass, minApiForLink));
+ // ...
// return TraversalContinuation.CONTINUE;
private static MethodTransformer getVisitMethodsTransformer(ParsedApiClass apiClass) {
+ IntBox lineNumberBox = new IntBox(0);
return replaceCode(
"placeHolderForVisitMethods",
transformer -> {
@@ -273,7 +315,14 @@
(apiLevel, references) -> {
references.forEach(
reference -> {
+ Label labelStart = new Label();
+ transformer.visitLabel(labelStart);
+ transformer.visitLineNumber(lineNumberBox.getAndIncrement(), labelStart);
transformer.visitVarInsn(ALOAD, 0);
+ transformer.visitVarInsn(ALOAD, 1);
+ transformer.visitVarInsn(ALOAD, 2);
+ transformer.visitVarInsn(ILOAD, 3);
+ transformer.visitLdcInsn(apiLevel.getLevel());
transformer.visitLdcInsn(reference.getMethodName());
List<TypeReference> formalTypes = reference.getFormalTypes();
transformer.visitLdcInsn(formalTypes.size());
@@ -289,16 +338,17 @@
} else {
transformer.visitInsn(ACONST_NULL);
}
- transformer.visitLdcInsn(apiLevel.getLevel());
- transformer.visitVarInsn(ALOAD, 1);
transformer.visitMethodInsn(
INVOKEVIRTUAL,
ANDROID_API_CLASS.getBinaryName(),
"visitMethod",
- "(Ljava/lang/String;"
- + "[Ljava/lang/String;Ljava/lang/String;"
+ "(Ljava/util/function/BiFunction;"
+ + descriptor(ClassReference.class)
+ "I"
- + "Ljava/util/function/BiFunction;)"
+ + "I"
+ + "Ljava/lang/String;"
+ + "[Ljava/lang/String;"
+ + "Ljava/lang/String;)"
+ TRAVERSAL_CONTINUATION.getDescriptor(),
false);
// Note that instead of storing the result here, we dup it on the stack.
@@ -324,9 +374,72 @@
transformer.visitInsn(POP);
});
});
+ if (!apiClass.isInterface()) {
+ // Visit super types before interfaces emulating a poor man's resolutions.
+ apiClass.visitSuperType(
+ (superType, apiLevel) -> {
+ addMembersForParent(
+ transformer,
+ superType,
+ "visitMethods",
+ apiLevel,
+ lineNumberBox.getAndIncrement());
+ });
+ }
+ apiClass.visitInterface(
+ (classReference, apiLevel) -> {
+ addMembersForParent(
+ transformer,
+ classReference,
+ "visitMethods",
+ apiLevel,
+ lineNumberBox.getAndIncrement());
+ });
});
}
+ private static void addMembersForParent(
+ MethodTransformer transformer,
+ ClassReference classReference,
+ String methodName,
+ AndroidApiLevel minApiLevel,
+ int lineNumber) {
+ String binaryName = fromTemplate(classReference).getBinaryName();
+ Label labelStart = new Label();
+ transformer.visitLabel(labelStart);
+ transformer.visitLineNumber(lineNumber, labelStart);
+ transformer.visitTypeInsn(NEW, binaryName);
+ transformer.visitInsn(DUP);
+ transformer.visitMethodInsn(INVOKESPECIAL, binaryName, "<init>", "()V", false);
+ transformer.visitVarInsn(ALOAD, 1);
+ transformer.visitVarInsn(ALOAD, 2);
+ // Compute the max api level compared to what is passed in.
+ transformer.visitLdcInsn(minApiLevel.getLevel());
+ transformer.visitVarInsn(ILOAD, 3);
+ transformer.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "max", "(II)I", false);
+ transformer.visitMethodInsn(
+ INVOKEVIRTUAL,
+ binaryName,
+ methodName,
+ "(Ljava/util/function/BiFunction;"
+ + descriptor(ClassReference.class)
+ + "I)"
+ + TRAVERSAL_CONTINUATION.getDescriptor(),
+ false);
+ // Note that instead of storing the result here, we dup it on the stack.
+ transformer.visitInsn(DUP);
+ transformer.visitMethodInsn(
+ INVOKEVIRTUAL, TRAVERSAL_CONTINUATION.getBinaryName(), "shouldBreak", "()Z", false);
+ Label label = new Label();
+ transformer.visitJumpInsn(IFEQ, label);
+ transformer.visitInsn(ARETURN);
+ transformer.visitLabel(label);
+ transformer.visitFrame(
+ F_SAME1, 0, new Object[] {}, 1, new Object[] {TRAVERSAL_CONTINUATION.getBinaryName()});
+ // The pop here is needed to remove the dupped value in the case we do not return.
+ transformer.visitInsn(POP);
+ }
+
// The transformer below changes AndroidApiDatabasePackageTemplate.buildClass from:
// placeHolder();
// return null;
@@ -359,8 +472,7 @@
false);
Label label = new Label();
transformer.visitJumpInsn(IFEQ, label);
- String binaryName =
- DescriptorUtils.getBinaryNameFromDescriptor(getApiClassDescriptor(apiClass));
+ String binaryName = getApiClassReference(apiClass).getBinaryName();
transformer.visitTypeInsn(NEW, binaryName);
transformer.visitInsn(DUP);
transformer.visitMethodInsn(INVOKESPECIAL, binaryName, "<init>", "()V", false);
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
index 2ad1e72..8b3571a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.apimodel.AndroidApiVersionsXmlParser.ParsedApiClass;
import com.android.tools.r8.cf.bootstrap.BootstrapCurrentEqualityTest;
+import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanBox;
@@ -36,7 +37,9 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.function.BiFunction;
import org.junit.Test;
@@ -121,6 +124,8 @@
* were introduced. Running main will generate a new jar and run tests on it to ensure it is
* compatible with R8 sources and works as expected.
*
+ * <p>The generated jar depends on r8NoManifestWithoutDeps.
+ *
* <p>If the generated jar passes tests it will be moved to third_party/android_jar/api-database/
* and override the current file in there.
*/
@@ -141,8 +146,12 @@
AndroidApiDatabaseBuilderGeneratorTest::testNoPlaceHolder);
tests.forEach(
test -> {
- if (!test.apply(generated, apiClasses)) {
- throw new RuntimeException("Generated jar did not pass tests");
+ try {
+ if (!test.apply(generated, apiClasses)) {
+ throw new RuntimeException("Generated jar did not pass tests");
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Generated jar did not pass tests", e);
}
});
}
@@ -232,44 +241,116 @@
}
private static String getExpected(List<ParsedApiClass> parsedApiClasses, boolean abort) {
+ Map<ClassReference, ParsedApiClass> parsedApiClassMap = new HashMap<>(parsedApiClasses.size());
+ parsedApiClasses.forEach(
+ parsedClass -> parsedApiClassMap.put(parsedClass.getClassReference(), parsedClass));
List<String> expected = new ArrayList<>();
parsedApiClasses.forEach(
apiClass -> {
- expected.add(apiClass.getClassReference().getDescriptor());
+ expected.add("CLASS: " + apiClass.getClassReference().getDescriptor());
expected.add(apiClass.getApiLevel().getName());
expected.add(apiClass.getTotalMemberCount() + "");
- BooleanBox added = new BooleanBox(false);
- apiClass.visitFieldReferences(
- (apiLevel, fieldReferences) -> {
- fieldReferences.forEach(
- fieldReference -> {
- if (added.isTrue() && abort) {
- return;
- }
- added.set();
- expected.add(fieldReference.getFieldType().getDescriptor());
- expected.add(fieldReference.getFieldName());
- expected.add(apiLevel.getName());
- });
- });
- added.set(false);
- apiClass.visitMethodReferences(
- (apiLevel, methodReferences) -> {
- methodReferences.forEach(
- methodReference -> {
- if (added.isTrue() && abort) {
- return;
- }
- added.set();
- expected.add(methodReference.getMethodDescriptor());
- expected.add(methodReference.getMethodName());
- expected.add(apiLevel.getName());
- });
- });
+ visitApiClass(expected, parsedApiClassMap, apiClass, apiClass.getApiLevel(), true, abort);
+ visitApiClass(
+ expected, parsedApiClassMap, apiClass, apiClass.getApiLevel(), false, abort);
});
return StringUtils.lines(expected);
}
+ private static boolean visitApiClass(
+ List<String> expected,
+ Map<ClassReference, ParsedApiClass> parsedApiClassMap,
+ ParsedApiClass apiClass,
+ AndroidApiLevel apiLevel,
+ boolean visitFields,
+ boolean abort) {
+ BooleanBox added = new BooleanBox(false);
+ if (visitFields) {
+ added.set(visitFields(expected, apiClass, apiLevel, abort));
+ } else {
+ added.set(visitMethods(expected, apiClass, apiLevel, abort));
+ }
+ if (added.isTrue() && abort) {
+ return true;
+ }
+ // Go through super type methods if not interface.
+ if (!apiClass.isInterface()) {
+ apiClass.visitSuperType(
+ (classReference, linkApiLevel) -> {
+ if (added.isTrue() && abort) {
+ return;
+ }
+ ParsedApiClass superApiClass = parsedApiClassMap.get(classReference);
+ assert superApiClass != null;
+ added.set(
+ visitApiClass(
+ expected,
+ parsedApiClassMap,
+ superApiClass,
+ linkApiLevel.max(apiLevel),
+ visitFields,
+ abort));
+ });
+ }
+ if (!visitFields) {
+ apiClass.visitInterface(
+ (classReference, linkApiLevel) -> {
+ if (added.isTrue() && abort) {
+ return;
+ }
+ ParsedApiClass ifaceApiClass = parsedApiClassMap.get(classReference);
+ assert ifaceApiClass != null;
+ added.set(
+ visitApiClass(
+ expected,
+ parsedApiClassMap,
+ ifaceApiClass,
+ linkApiLevel.max(apiLevel),
+ visitFields,
+ abort));
+ });
+ }
+ return added.get();
+ }
+
+ private static boolean visitFields(
+ List<String> expected, ParsedApiClass apiClass, AndroidApiLevel minApiLevel, boolean abort) {
+ BooleanBox added = new BooleanBox(false);
+ apiClass.visitFieldReferences(
+ (apiLevel, fieldReferences) -> {
+ fieldReferences.forEach(
+ fieldReference -> {
+ if (added.isTrue() && abort) {
+ return;
+ }
+ added.set();
+ expected.add(fieldReference.getFieldType().getDescriptor());
+ expected.add(fieldReference.getFieldName());
+ expected.add(apiLevel.max(minApiLevel).getName());
+ });
+ });
+ return added.get();
+ }
+
+ private static boolean visitMethods(
+ List<String> expected, ParsedApiClass apiClass, AndroidApiLevel minApiLevel, boolean abort) {
+ BooleanBox added = new BooleanBox(false);
+ apiClass.visitMethodReferences(
+ (apiLevel, methodReferences) -> {
+ methodReferences.forEach(
+ methodReference -> {
+ if (added.isTrue() && abort) {
+ return;
+ }
+ added.set();
+ expected.add(methodReference.getMethodDescriptor());
+ expected.add(methodReference.getMethodName());
+ expected.add(apiLevel.max(minApiLevel).getName());
+ });
+ });
+ return added.get();
+ }
+
public static class TestGeneratedMainVisitClasses {
public static void main(String[] args) {
@@ -286,7 +367,7 @@
AndroidApiDatabaseBuilderTemplate.buildClass(
Reference.classFromDescriptor(descriptor));
if (apiClass != null) {
- System.out.println(descriptor);
+ System.out.println("CLASS: " + descriptor);
System.out.println(apiClass.getApiLevel().getName());
System.out.println(apiClass.getMemberCount());
apiClass.visitFields(
@@ -317,7 +398,7 @@
AndroidApiDatabaseBuilderTemplate.buildClass(
Reference.classFromDescriptor(descriptor));
if (apiClass != null) {
- System.out.println(descriptor);
+ System.out.println("CLASS: " + descriptor);
System.out.println(apiClass.getApiLevel().getName());
System.out.println(apiClass.getMemberCount());
apiClass.visitFields(
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseClassTemplate.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseClassTemplate.java
index faefd62..1a8b7e3 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseClassTemplate.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseClassTemplate.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.apimodel;
import com.android.tools.r8.androidapi.AndroidApiClass;
+import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
@@ -34,7 +35,9 @@
@Override
public TraversalContinuation visitFields(
- BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor) {
+ BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor,
+ ClassReference holder,
+ int minApi) {
// Code added dynamically in AndroidApiDatabaseBuilderGenerator.
placeHolderForVisitFields();
return TraversalContinuation.CONTINUE;
@@ -42,7 +45,9 @@
@Override
public TraversalContinuation visitMethods(
- BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor) {
+ BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor,
+ ClassReference holder,
+ int minApi) {
// Code added dynamically in AndroidApiDatabaseBuilderGenerator.
placeHolderForVisitMethods();
return TraversalContinuation.CONTINUE;
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
index 4002bf9..24b9613 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
@@ -19,6 +19,7 @@
import java.io.File;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@@ -40,8 +41,9 @@
this.maxApiLevel = maxApiLevel;
}
- private ParsedApiClass register(ClassReference reference, AndroidApiLevel apiLevel) {
- ParsedApiClass parsedApiClass = new ParsedApiClass(reference, apiLevel);
+ private ParsedApiClass register(
+ ClassReference reference, AndroidApiLevel apiLevel, boolean isInterface) {
+ ParsedApiClass parsedApiClass = new ParsedApiClass(reference, apiLevel, isInterface);
classes.add(parsedApiClass);
return parsedApiClass;
}
@@ -62,11 +64,19 @@
continue;
}
ClassReference originalReference = clazz.getOriginalReference();
- ParsedApiClass parsedApiClass = register(originalReference, apiLevel);
+ ParsedApiClass parsedApiClass = register(originalReference, apiLevel, clazz.isInterface());
NodeList members = node.getChildNodes();
for (int j = 0; j < members.getLength(); j++) {
Node memberNode = members.item(j);
- if (isMethod(memberNode)) {
+ if (isExtends(memberNode)) {
+ parsedApiClass.registerSuperType(
+ Reference.classFromBinaryName(getName(memberNode)),
+ hasSince(memberNode) ? getSince(memberNode) : apiLevel);
+ } else if (isImplements(memberNode)) {
+ parsedApiClass.registerInterface(
+ Reference.classFromBinaryName(getName(memberNode)),
+ hasSince(memberNode) ? getSince(memberNode) : apiLevel);
+ } else if (isMethod(memberNode)) {
// TODO(b/190326408): Check for existence.
parsedApiClass.register(
getMethodReference(originalReference, memberNode),
@@ -109,16 +119,29 @@
return node.getNodeName().equals("field");
}
- private AndroidApiLevel getMaxAndroidApiLevelFromNode(Node node, AndroidApiLevel defaultValue) {
- if (node == null) {
- return defaultValue;
- }
+ private boolean isExtends(Node node) {
+ return node.getNodeName().equals("extends");
+ }
+
+ private boolean isImplements(Node node) {
+ return node.getNodeName().equals("implements");
+ }
+
+ private boolean hasSince(Node node) {
+ return node.getAttributes().getNamedItem("since") != null;
+ }
+
+ private AndroidApiLevel getSince(Node node) {
+ assert hasSince(node);
Node since = node.getAttributes().getNamedItem("since");
- if (since == null) {
+ return AndroidApiLevel.getAndroidApiLevel(Integer.parseInt(since.getNodeValue()));
+ }
+
+ private AndroidApiLevel getMaxAndroidApiLevelFromNode(Node node, AndroidApiLevel defaultValue) {
+ if (node == null || !hasSince(node)) {
return defaultValue;
}
- return defaultValue.max(
- AndroidApiLevel.getAndroidApiLevel(Integer.parseInt(since.getNodeValue())));
+ return defaultValue.max(getSince(node));
}
public static List<ParsedApiClass> getParsedApiClasses(
@@ -132,12 +155,17 @@
private final ClassReference classReference;
private final AndroidApiLevel apiLevel;
+ private final boolean isInterface;
+ private final Map<ClassReference, AndroidApiLevel> superTypes = new LinkedHashMap<>();
+ private final Map<ClassReference, AndroidApiLevel> interfaces = new LinkedHashMap<>();
private final TreeMap<AndroidApiLevel, List<FieldReference>> fieldReferences = new TreeMap<>();
private final Map<AndroidApiLevel, List<MethodReference>> methodReferences = new TreeMap<>();
- private ParsedApiClass(ClassReference classReference, AndroidApiLevel apiLevel) {
+ private ParsedApiClass(
+ ClassReference classReference, AndroidApiLevel apiLevel, boolean isInterface) {
this.classReference = classReference;
this.apiLevel = apiLevel;
+ this.isInterface = isInterface;
}
public ClassReference getClassReference() {
@@ -171,6 +199,16 @@
methodReferences.computeIfAbsent(apiLevel, ignoreArgument(ArrayList::new)).add(reference);
}
+ private void registerSuperType(ClassReference superType, AndroidApiLevel apiLevel) {
+ AndroidApiLevel existing = superTypes.put(superType, apiLevel);
+ assert existing == null;
+ }
+
+ private void registerInterface(ClassReference iface, AndroidApiLevel apiLevel) {
+ AndroidApiLevel existing = interfaces.put(iface, apiLevel);
+ assert existing == null;
+ }
+
public void visitFieldReferences(BiConsumer<AndroidApiLevel, List<FieldReference>> consumer) {
fieldReferences.forEach(
(apiLevel, references) -> {
@@ -186,5 +224,17 @@
consumer.accept(apiLevel, references);
});
}
+
+ public void visitSuperType(BiConsumer<ClassReference, AndroidApiLevel> consumer) {
+ superTypes.forEach(consumer);
+ }
+
+ public void visitInterface(BiConsumer<ClassReference, AndroidApiLevel> consumer) {
+ interfaces.forEach(consumer);
+ }
+
+ public boolean isInterface() {
+ return isInterface;
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldSuperTypeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldSuperTypeTest.java
index 32b111e..5b47c79 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldSuperTypeTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldSuperTypeTest.java
@@ -5,10 +5,8 @@
package com.android.tools.r8.apimodel;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.addTracedApiReferenceLevelCallBack;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -35,7 +33,7 @@
this.parameters = parameters;
}
- @Test(expected = CompilationFailedException.class)
+ @Test
public void testR8() throws Exception {
Method main = Main.class.getDeclaredMethod("main", String[].class);
testForR8(parameters.getBackend())
@@ -55,16 +53,14 @@
addTracedApiReferenceLevelCallBack(
(method, apiLevel) -> {
if (Reference.methodFromMethod(main).equals(method)) {
- // TODO(b/193414761): Should not be UNKNOWN.
- assertEquals(AndroidApiLevel.UNKNOWN, apiLevel);
+ assertEquals(
+ parameters.isCfRuntime()
+ ? AndroidApiLevel.E
+ : parameters.getApiLevel().max(AndroidApiLevel.E),
+ apiLevel);
}
}))
- .compileWithExpectedDiagnostics(
- diagnostics -> {
- // TODO(b/193414761): We should analyze all members.
- diagnostics.assertErrorMessageThatMatches(
- containsString("Every member should have been analyzed"));
- });
+ .compile();
}
/* Only here to get the test to compile */
@@ -77,7 +73,7 @@
public static void main(String[] args) {
// START_CONTINUATION_MASK is inherited from android/app/Service which was introduced at
- // AndroidApiLevel.B.
+ // AndroidApiLevel.E.
System.out.println(
new /* android.accessibilityservice */ AccessibilityService().START_CONTINUATION_MASK);
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchInterfaceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchInterfaceTest.java
index 22c0702..892f9d8 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchInterfaceTest.java
@@ -5,15 +5,13 @@
package com.android.tools.r8.apimodel;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.addTracedApiReferenceLevelCallBack;
-import static com.android.tools.r8.utils.AndroidApiLevel.UNKNOWN;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
import java.lang.reflect.Method;
import java.util.ArrayList;
import org.junit.Test;
@@ -35,7 +33,7 @@
this.parameters = parameters;
}
- @Test(expected = CompilationFailedException.class)
+ @Test
public void testR8() throws Exception {
Method main = Main.class.getDeclaredMethod("main", String[].class);
testForR8(parameters.getBackend())
@@ -48,16 +46,14 @@
addTracedApiReferenceLevelCallBack(
(method, apiLevel) -> {
if (Reference.methodFromMethod(main).equals(method)) {
- // TODO(b/193414761): Should not be UNKNOWN.
- assertEquals(UNKNOWN, apiLevel);
+ assertEquals(
+ parameters.isCfRuntime()
+ ? AndroidApiLevel.B
+ : parameters.getApiLevel().max(AndroidApiLevel.B),
+ apiLevel);
}
}))
- .compileWithExpectedDiagnostics(
- diagnostics -> {
- // TODO(b/193414761): We should analyze all members.
- diagnostics.assertErrorMessageThatMatches(
- containsString("Every member should have been analyzed"));
- });
+ .compile();
}
public static class Main {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java
index 3f8bed1..04337ec 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java
@@ -6,11 +6,9 @@
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.addTracedApiReferenceLevelCallBack;
import static com.android.tools.r8.utils.AndroidApiLevel.LATEST;
-import static com.android.tools.r8.utils.AndroidApiLevel.UNKNOWN;
-import static org.hamcrest.CoreMatchers.containsString;
+import static com.android.tools.r8.utils.AndroidApiLevel.R;
import static org.junit.Assert.assertEquals;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -36,7 +34,7 @@
this.parameters = parameters;
}
- @Test(expected = CompilationFailedException.class)
+ @Test
public void testR8() throws Exception {
// Landroid/view/accessibility/AccessibilityNodeInfo$AccessibilityAction; is introduced at api
// level 21 and on api level 30 it implements android.os.Parcelable.
@@ -58,16 +56,10 @@
addTracedApiReferenceLevelCallBack(
(method, apiLevel) -> {
if (Reference.methodFromMethod(main).equals(method)) {
- // TODO(b/193414761): Should be 30.
- assertEquals(UNKNOWN, apiLevel);
+ assertEquals(R, apiLevel);
}
}))
- .compileWithExpectedDiagnostics(
- diagnostics -> {
- // TODO(b/193414761): We should analyze all members.
- diagnostics.assertErrorMessageThatMatches(
- containsString("Every member should have been analyzed"));
- });
+ .compile();
}
public static class AccessibilityNodeInfo$AccessibilityAction {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchSuperTypeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchSuperTypeTest.java
index 2c5868e..c79c69b 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchSuperTypeTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchSuperTypeTest.java
@@ -5,10 +5,8 @@
package com.android.tools.r8.apimodel;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.addTracedApiReferenceLevelCallBack;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -35,7 +33,7 @@
this.parameters = parameters;
}
- @Test(expected = CompilationFailedException.class)
+ @Test
public void testR8() throws Exception {
Method main = Main.class.getDeclaredMethod("main", String[].class);
testForR8(parameters.getBackend())
@@ -55,16 +53,17 @@
addTracedApiReferenceLevelCallBack(
(method, apiLevel) -> {
if (Reference.methodFromMethod(main).equals(method)) {
- // TODO(b/193414761): Should not be UNKNOWN.
- assertEquals(AndroidApiLevel.UNKNOWN, apiLevel);
+ // android.app.Service.stopSelf() was introduced at AndroidApiLevel.B but
+ // android/accessibilityservice/AccessibilityService was introduced at D
+ // so the minimum api level is D.
+ assertEquals(
+ parameters.isCfRuntime()
+ ? AndroidApiLevel.D
+ : parameters.getApiLevel().max(AndroidApiLevel.D),
+ apiLevel);
}
}))
- .compileWithExpectedDiagnostics(
- diagnostics -> {
- // TODO(b/193414761): We should analyze all members.
- diagnostics.assertErrorMessageThatMatches(
- containsString("Every member should have been analyzed"));
- });
+ .compile();
}
/* Only here to get the test to compile */
@@ -76,7 +75,8 @@
public static class Main {
public static void main(String[] args) {
- // stopSelf() is inherited from android/app/Service which was introduced at AndroidApiLevel.B.
+ // AccessibilityService.stopSelf() is inherited from android/app/Service which was introduced
+ // at AndroidApiLevel.B.
new /* android.accessibilityservice */ AccessibilityService().stopSelf();
}
}
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/AbstractAfterTreeShakingBridgeHoistingTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/AbstractAfterTreeShakingBridgeHoistingTest.java
new file mode 100644
index 0000000..772ef4f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/AbstractAfterTreeShakingBridgeHoistingTest.java
@@ -0,0 +1,120 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.bridgeremoval.hoisting;
+
+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 org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/** Regression test for b/195037294. */
+@RunWith(Parameterized.class)
+public class AbstractAfterTreeShakingBridgeHoistingTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public AbstractAfterTreeShakingBridgeHoistingTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class, A.class)
+ .addProgramClassFileData(
+ transformer(B1.class)
+ .setBridge(B1.class.getDeclaredMethod("virtualBridge", Object.class))
+ .transform(),
+ transformer(B2.class)
+ .setBridge(B2.class.getDeclaredMethod("virtualBridge", Object.class))
+ .transform())
+ .addKeepMainRule(TestClass.class)
+ // Keep test() method to disable call site optimization for that method.
+ .addKeepRules(
+ "-keepclassmembers class " + TestClass.class.getTypeName() + " {",
+ " void test(...);",
+ "}")
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput("Hello");
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ B1 b1 = null;
+ B2 b2 = null;
+ // Dead instantiations of B1 and B2. This way, R8 considers B1 and B2 to be instantiated until
+ // the second round of tree shaking. After the second round of tree shaking, the
+ // virtualBridge() methods on B1 and B2 are made abstract, since they are not instantiated.
+ // This should disable bridge hoisting.
+ if (alwaysFalse()) {
+ b1 = B1.create();
+ b2 = B2.create();
+ }
+ test(new A(), b1, b2);
+ }
+
+ static boolean alwaysFalse() {
+ return false;
+ }
+
+ @NeverInline
+ private static void test(A a, B1 b1, B2 b2) {
+ System.out.print(a.m("Hello"));
+ if (b1 != null) {
+ System.out.print(b1.virtualBridge(" "));
+ }
+ if (b2 != null) {
+ System.out.println(b2.virtualBridge("world!"));
+ }
+ }
+ }
+
+ static class A {
+
+ @NeverInline
+ public Object m(String arg) {
+ return System.currentTimeMillis() >= 0 ? arg : null;
+ }
+ }
+
+ @NeverClassInline
+ static class B1 extends A {
+
+ static B1 create() {
+ return new B1();
+ }
+
+ @NeverInline
+ public /*bridge*/ String virtualBridge(Object o) {
+ return (String) m((String) o);
+ }
+ }
+
+ @NeverClassInline
+ static class B2 extends A {
+
+ static B2 create() {
+ return new B2();
+ }
+
+ @NeverInline
+ public /*bridge*/ String virtualBridge(Object o) {
+ return (String) m((String) o);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
index 68baadf..e7c5ce1 100644
--- a/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
@@ -17,7 +17,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.LookupResult;
import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.google.common.collect.ImmutableList;
@@ -76,7 +76,7 @@
// }
AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(I.class, I.class);
DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory());
- ResolutionResult resolutionResult = appView.appInfo().resolveMethodOnInterface(method);
+ MethodResolutionResult resolutionResult = appView.appInfo().resolveMethodOnInterface(method);
DexType typeI = buildType(I.class, appView.dexItemFactory());
DexType typeL = buildType(L.class, appView.dexItemFactory());
DexType typeA = buildType(A.class, appView.dexItemFactory());
@@ -117,7 +117,7 @@
// }
AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(I.class, I.class);
DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory());
- ResolutionResult resolutionResult = appView.appInfo().resolveMethodOnInterface(method);
+ MethodResolutionResult resolutionResult = appView.appInfo().resolveMethodOnInterface(method);
DexType typeI = buildType(I.class, appView.dexItemFactory());
DexType typeL = buildType(L.class, appView.dexItemFactory());
DexType typeA = buildType(A.class, appView.dexItemFactory());
@@ -157,7 +157,7 @@
// }
AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(J.class, J.class);
DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory());
- ResolutionResult resolutionResult = appView.appInfo().resolveMethodOnInterface(method);
+ MethodResolutionResult resolutionResult = appView.appInfo().resolveMethodOnInterface(method);
DexType typeI = buildType(I.class, appView.dexItemFactory());
DexType typeB = buildType(A.class, appView.dexItemFactory());
DexProgramClass classI = appView.definitionForProgramType(typeI);
@@ -194,7 +194,7 @@
// }
AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(J.class, A.class);
DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory());
- ResolutionResult resolutionResult = appView.appInfo().resolveMethodOnInterface(method);
+ MethodResolutionResult resolutionResult = appView.appInfo().resolveMethodOnInterface(method);
DexType typeI = buildType(I.class, appView.dexItemFactory());
DexType typeB = buildType(A.class, appView.dexItemFactory());
DexProgramClass classI = appView.definitionForProgramType(typeI);
@@ -233,7 +233,7 @@
// }
AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(I.class, I.class);
DexMethod method = buildNullaryVoidMethod(I.class, "foo", appView.dexItemFactory());
- ResolutionResult resolutionResult = appView.appInfo().resolveMethodOnInterface(method);
+ MethodResolutionResult resolutionResult = appView.appInfo().resolveMethodOnInterface(method);
DexType typeI = buildType(I.class, appView.dexItemFactory());
DexType typeB = buildType(A.class, appView.dexItemFactory());
DexProgramClass classI = appView.definitionForProgramType(typeI);
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java
index 7b5437e..0ea8597 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java
@@ -14,10 +14,6 @@
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.classmerging.horizontal.HorizontalClassMergingTestBase;
-import com.android.tools.r8.classmerging.horizontal.dispatch.OverrideDefaultMethodTest.A;
-import com.android.tools.r8.classmerging.horizontal.dispatch.OverrideDefaultMethodTest.B;
-import com.android.tools.r8.classmerging.horizontal.dispatch.OverrideDefaultMethodTest.I;
-import com.android.tools.r8.classmerging.horizontal.dispatch.OverrideDefaultMethodTest.J;
import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import org.junit.Test;
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerInitTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerInitTest.java
index 6e93ac6..b42cd2e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerInitTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerInitTest.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
@@ -54,6 +55,8 @@
// The initializer is small in this example so only allow FORCE inlining.
options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
})
+ .addVerticallyMergedClassesInspector(
+ VerticallyMergedClassesInspector::assertNoClassesMerged)
.compile()
.inspect(
inspector -> {
diff --git a/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java b/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java
index 4b72ec5..b79721f 100644
--- a/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java
+++ b/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java
@@ -75,7 +75,6 @@
builder,
testBuilder ->
testBuilder
- .addKeepMainRule(CLASS_NAME)
// Add main dex rule to disable Class.forName() optimization.
.addMainDexRules("-keep class " + CLASS_NAME)
.addDontOptimize()
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicTest.java
new file mode 100644
index 0000000..ee5142f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicTest.java
@@ -0,0 +1,238 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.desugar.constantdynamic;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticOrigin;
+import static com.android.tools.r8.utils.DescriptorUtils.JAVA_PACKAGE_SEPARATOR;
+import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ZipUtils;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class JacocoConstantDynamicTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameter(1)
+ public boolean useConstantDynamic;
+
+ @Parameters(name = "{0}, useConstantDynamic: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(),
+ BooleanUtils.values());
+ }
+
+ public static JacocoClasses testClassesNoConstantDynamic;
+ public static JacocoClasses testClassesConstantDynamic;
+
+ public JacocoClasses testClasses;
+
+ private static final String MAIN_CLASS = TestRunner.class.getTypeName();
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("Hello, world!");
+
+ @BeforeClass
+ public static void setUpInput() throws IOException {
+ testClassesNoConstantDynamic = testClasses(getStaticTemp(), CfVersion.V1_8);
+ testClassesConstantDynamic = testClasses(getStaticTemp(), CfVersion.V11);
+ }
+
+ @Before
+ public void setUp() throws IOException {
+ testClasses = useConstantDynamic ? testClassesConstantDynamic : testClassesNoConstantDynamic;
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ assumeTrue(
+ parameters.isCfRuntime()
+ && (parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11)
+ || !useConstantDynamic));
+
+ // Run non-instrumented code.
+ testForRuntime(parameters)
+ .addProgramFiles(testClasses.getOriginal())
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+
+ // Run instrumented code without an agent.
+ testForRuntime(parameters)
+ .addProgramFiles(testClasses.getInstrumented())
+ .addProgramFiles(ToolHelper.JACOCO_AGENT)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+
+ // Run non-instrumented code with an agent causing on the fly instrumentation on the JVM.
+ Path output = temp.newFolder().toPath();
+ Path agentOutputOnTheFly = output.resolve("on-the-fly");
+ testForJvm()
+ .addProgramFiles(testClasses.getOriginal())
+ .enableJaCoCoAgent(ToolHelper.JACOCO_AGENT, agentOutputOnTheFly)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ List<String> onTheFlyReport = testClasses.generateReport(agentOutputOnTheFly);
+ assertEquals(2, onTheFlyReport.size());
+
+ // Run the instrumented code with offline instrumentation turned on.
+ Path agentOutputOffline = output.resolve("offline");
+ testForJvm()
+ .addProgramFiles(testClasses.getInstrumented())
+ .enableJaCoCoAgentForOfflineInstrumentedCode(ToolHelper.JACOCO_AGENT, agentOutputOffline)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ List<String> offlineReport = testClasses.generateReport(agentOutputOffline);
+ assertEquals(onTheFlyReport, offlineReport);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ assumeTrue(parameters.getRuntime().isDex());
+ if (!useConstantDynamic) {
+ Path output = temp.newFolder().toPath();
+ Path agentOutput = output.resolve("jacoco.exec");
+ testForD8(parameters.getBackend())
+ .addProgramFiles(testClasses.getInstrumented())
+ .addProgramFiles(ToolHelper.JACOCO_AGENT)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .runWithJaCoCo(agentOutput, parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ // TODO(sgjesse): Need to figure out why there is no instrumentation output for newer VMs.
+ if (parameters.getRuntime().asDex().getVm().isOlderThanOrEqual(DexVm.ART_4_4_4_HOST)) {
+ List<String> report = testClasses.generateReport(agentOutput);
+ assertEquals(2, report.size());
+ } else {
+ assertFalse(Files.exists(agentOutput));
+ }
+ } else {
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ testForD8(parameters.getBackend())
+ .addProgramFiles(testClasses.getInstrumented())
+ .addProgramFiles(ToolHelper.JACOCO_AGENT)
+ .setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ // Check that the error is reported as an error to the diagnostics handler.
+ diagnostics.assertErrorsCount(1);
+ diagnostics.assertAllErrorsMatch(
+ allOf(
+ diagnosticMessage(containsString("Unsupported dynamic constant")),
+ // The fatal error is not given an origin, so it can't provide it.
+ // Note: This could be fixed by delaying reporting and associate the
+ // info
+ // at the top-level handler. It would require mangling of the
+ // diagnostic,
+ // so maybe not that elegant.
+ diagnosticOrigin(Origin.unknown())));
+ diagnostics.assertWarningsCount(0);
+ diagnostics.assertInfosCount(0);
+ }));
+ }
+ }
+
+ private static JacocoClasses testClasses(TemporaryFolder temp, CfVersion version)
+ throws IOException {
+ return new JacocoClasses(
+ transformer(TestRunner.class)
+ .setVersion(version) /*.setClassDescriptor("LTestRunner;")*/
+ .transform(),
+ temp);
+ }
+
+ // Two sets of class files with and without JaCoCo off line instrumentation.
+ private static class JacocoClasses {
+ private final Path dir;
+
+ private final Path originalJar;
+ private final Path instrumentedJar;
+
+ // Create JacocoClasses with just one class provided as bytes.
+ private JacocoClasses(byte[] clazz, TemporaryFolder temp) throws IOException {
+ dir = temp.newFolder().toPath();
+
+ // Write the class to a .class file with package sub-directories.
+ String typeName = extractClassName(clazz);
+ int lastDotIndex = typeName.lastIndexOf('.');
+ String pkg = typeName.substring(0, lastDotIndex);
+ String baseFileName = typeName.substring(lastDotIndex + 1) + CLASS_EXTENSION;
+ Path original = dir.resolve("original");
+ Files.createDirectories(original);
+ Path packageDir = original.resolve(pkg.replace(JAVA_PACKAGE_SEPARATOR, File.separatorChar));
+ Files.createDirectories(packageDir);
+ Path classFile = packageDir.resolve(baseFileName);
+ Files.write(classFile, clazz);
+
+ // Run offline instrumentation.
+ Path instrumented = dir.resolve("instrumented");
+ Files.createDirectories(instrumented);
+ runJacocoInstrumentation(original, instrumented);
+ originalJar = dir.resolve("original" + JAR_EXTENSION);
+ ZipUtils.zip(originalJar, original);
+ instrumentedJar = dir.resolve("instrumented" + JAR_EXTENSION);
+ ZipUtils.zip(instrumentedJar, instrumented);
+ }
+
+ public Path getOriginal() {
+ return originalJar;
+ }
+
+ public Path getInstrumented() {
+ return instrumentedJar;
+ }
+
+ public List<String> generateReport(Path jacocoExec) throws IOException {
+ Path report = dir.resolve("report.scv");
+ ProcessResult result = ToolHelper.runJaCoCoReport(originalJar, jacocoExec, report);
+ assertEquals(result.toString(), 0, result.exitCode);
+ return Files.readAllLines(report);
+ }
+
+ private void runJacocoInstrumentation(Path input, Path outdir) throws IOException {
+ ProcessResult result = ToolHelper.runJaCoCoInstrument(input, outdir);
+ assertEquals(result.toString(), 0, result.exitCode);
+ }
+ }
+
+ static class TestRunner {
+
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
index ddebca5..fdca331 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
@@ -126,7 +126,7 @@
Path desugaredLib =
getDesugaredLibraryInCF(parameters, this::configurationForLibraryCompilation);
- // Run on the JVM with desuagred library on classpath.
+ // Run on the JVM with desugared library on classpath.
testForJvm()
.addProgramFiles(jar)
.addRunClasspathFiles(desugaredLib)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideInLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideInLibraryTest.java
index 1f4d791..a23a6d8 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideInLibraryTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideInLibraryTest.java
@@ -37,7 +37,7 @@
@RunWith(Parameterized.class)
public class DefaultMethodOverrideInLibraryTest extends DesugaredLibraryTestBase {
- static final String EXPECTED = StringUtils.lines("0", "42");
+ static final String EXPECTED = StringUtils.lines("0", "42", "0", "0", "42", "42");
private final TestParameters parameters;
@@ -83,7 +83,7 @@
if (parameters.isDexRuntime()
&& parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)
&& parameters.getRuntime().asDex().getVm().getVersion().equals(Version.V7_0_0)) {
- result.assertSuccessWithOutputLines("42", "42");
+ result.assertSuccessWithOutputLines("42", "42", "0", "0", "42", "42");
return;
}
result.assertSuccessWithOutput(EXPECTED);
@@ -127,16 +127,32 @@
static class MyIntegerArrayListWithoutOverride extends ArrayList<Integer>
implements MyIntegerList {
// No override of spliterator.
+
+ public Spliterator<Integer> superSpliteratorItf() {
+ return MyIntegerList.super.spliterator();
+ }
+
+ public Spliterator<Integer> superSpliterator() {
+ return super.spliterator();
+ }
}
// Derived list with an override of spliterator. The call must hit the classes override and that
- // will explictly call the custom default method.
+ // will explicitly call the custom default method.
static class MyIntegerArrayListWithOverride extends ArrayList<Integer> implements MyIntegerList {
@Override
public Spliterator<Integer> spliterator() {
return MyIntegerList.super.spliterator();
}
+
+ public Spliterator<Integer> superSpliteratorItf() {
+ return MyIntegerList.super.spliterator();
+ }
+
+ public Spliterator<Integer> superSpliterator() {
+ return super.spliterator();
+ }
}
static class Main {
@@ -144,6 +160,11 @@
public static void main(String[] args) {
System.out.println(new MyIntegerArrayListWithoutOverride().spliterator().estimateSize());
System.out.println(new MyIntegerArrayListWithOverride().spliterator().estimateSize());
+ System.out.println(new MyIntegerArrayListWithoutOverride().superSpliterator().estimateSize());
+ System.out.println(new MyIntegerArrayListWithOverride().superSpliterator().estimateSize());
+ System.out.println(
+ new MyIntegerArrayListWithoutOverride().superSpliteratorItf().estimateSize());
+ System.out.println(new MyIntegerArrayListWithOverride().superSpliteratorItf().estimateSize());
}
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FreezePeriodTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FreezePeriodTest.java
new file mode 100644
index 0000000..ac48549
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FreezePeriodTest.java
@@ -0,0 +1,208 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.desugar.desugaredlibrary.conversiontests;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
+import java.time.MonthDay;
+import java.util.List;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class FreezePeriodTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+ private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.O;
+ private static final String EXPECTED_RESULT =
+ StringUtils.lines(
+ "FP:--05-05;--06-06",
+ "FP:--05-05;--06-06",
+ "FP:--05-05;--06-0601",
+ "MFP:--05-05;--06-06");
+ private static Path CUSTOM_LIB;
+
+ @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getConversionParametersUpToExcluding(MIN_SUPPORTED), BooleanUtils.values());
+ }
+
+ public FreezePeriodTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @BeforeClass
+ public static void compileCustomLib() throws Exception {
+ CUSTOM_LIB =
+ testForD8(getStaticTemp())
+ .addProgramClasses(FreezePeriod.class)
+ .setMinApi(MIN_SUPPORTED)
+ .compile()
+ .writeToZip();
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8()
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .setMinApi(parameters.getApiLevel())
+ .addProgramClasses(Executor.class, MyFreezePeriod.class)
+ .addLibraryClasses(FreezePeriod.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .addRunClasspathFiles(CUSTOM_LIB)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testD8CfToCf() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ Path jar =
+ testForD8(Backend.CF)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .setMinApi(parameters.getApiLevel())
+ .addProgramClasses(Executor.class, MyFreezePeriod.class)
+ .addLibraryClasses(FreezePeriod.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .writeToZip();
+ String desugaredLibraryKeepRules = "";
+ if (shrinkDesugaredLibrary && keepRuleConsumer.get() != null) {
+ // Collection keep rules is only implemented in the DEX writer.
+ assertEquals(0, keepRuleConsumer.get().length());
+ desugaredLibraryKeepRules = "-keep class * { *; }";
+ }
+ if (parameters.getRuntime().isDex()) {
+ testForD8()
+ .addProgramFiles(jar)
+ .setMinApi(parameters.getApiLevel())
+ .disableDesugaring()
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ desugaredLibraryKeepRules,
+ shrinkDesugaredLibrary)
+ .addRunClasspathFiles(CUSTOM_LIB)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ } else {
+ testForJvm()
+ .addProgramFiles(jar)
+ .addRunClasspathFiles(getDesugaredLibraryInCF(parameters, options -> {}))
+ .addRunClasspathFiles(CUSTOM_LIB)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(parameters.getBackend())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Executor.class)
+ .addProgramClasses(Executor.class, MyFreezePeriod.class)
+ .addLibraryClasses(FreezePeriod.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .allowStdoutMessages()
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .addRunClasspathFiles(CUSTOM_LIB)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ static class Executor {
+
+ public static void main(String[] args) {
+ testConversion2ArgsOrLess();
+ testConversion3ArgsOrMore();
+ testConversionWithValuesOnStack();
+ testConversionSuperInit();
+ }
+
+ private static void testConversionSuperInit() {
+ MyFreezePeriod myFreezePeriod = new MyFreezePeriod(MonthDay.of(5, 5), MonthDay.of(6, 6));
+ System.out.println(myFreezePeriod.print());
+ }
+
+ private static void testConversionWithValuesOnStack() {
+ print(0, new FreezePeriod(MonthDay.of(5, 5), MonthDay.of(6, 6)), 1);
+ }
+
+ private static void testConversion3ArgsOrMore() {
+ FreezePeriod freezePeriod2 = new FreezePeriod(MonthDay.of(5, 5), MonthDay.of(6, 6), 0, 1);
+ System.out.println(freezePeriod2.print());
+ }
+
+ private static void testConversion2ArgsOrLess() {
+ FreezePeriod freezePeriod = new FreezePeriod(MonthDay.of(5, 5), MonthDay.of(6, 6));
+ System.out.println(freezePeriod.print());
+ }
+
+ static void print(int i1, FreezePeriod freezePeriod, int i2) {
+ System.out.println(freezePeriod.print() + i1 + i2);
+ }
+ }
+
+ static class MyFreezePeriod extends FreezePeriod {
+
+ public MyFreezePeriod(MonthDay start, MonthDay end) {
+ super(start, end);
+ }
+
+ public String print() {
+ return "M" + super.print();
+ }
+ }
+
+ // This class will be put at compilation time as library and on the runtime class path.
+ // This class is convenient for easy testing. Each method plays the role of methods in the
+ // platform APIs for which argument/return values need conversion.
+ static class FreezePeriod {
+
+ private final MonthDay start;
+ private final MonthDay end;
+
+ public FreezePeriod(MonthDay start, MonthDay end) {
+ this.start = start;
+ this.end = end;
+ }
+
+ public FreezePeriod(MonthDay start, MonthDay end, int extra1, Integer extra2) {
+ this.start = start;
+ this.end = end;
+ }
+
+ public String print() {
+ return "FP:" + start + ";" + end;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
index 3409cfd..e1592c0 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
+import java.nio.file.Path;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.Date;
@@ -15,6 +16,7 @@
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntUnaryOperator;
+import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -29,7 +31,8 @@
@Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
public static List<Object[]> data() {
return buildParameters(
- BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ BooleanUtils.values(),
+ getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
}
public RetargetOverrideTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
@@ -38,7 +41,65 @@
}
@Test
+ public void testRetargetOverrideCf() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ Path desugaredTwice =
+ testForD8(Backend.CF)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .addProgramFiles(
+ testForD8(Backend.CF)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .addInnerClasses(RetargetOverrideTest.class)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .writeToZip())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(
+ options -> options.desugarSpecificOptions().allowAllDesugaredInput = true)
+ .compile()
+ .writeToZip();
+
+ String stdout;
+ if (parameters.getRuntime().isDex()) {
+ // Convert to DEX without desugaring and run.
+ stdout =
+ testForD8(Backend.DEX)
+ .addProgramFiles(desugaredTwice)
+ .setMinApi(parameters.getApiLevel())
+ .disableDesugaring()
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(
+ parameters.getRuntime(),
+ Executor.class,
+ Boolean.toString(parameters.getRuntime().isCf()))
+ .assertSuccess()
+ .getStdOut();
+ } else {
+ // Run on the JVM with desugared library on classpath.
+ stdout =
+ testForJvm()
+ .addProgramFiles(desugaredTwice)
+ .addRunClasspathFiles(buildDesugaredLibraryClassFile(parameters.getApiLevel()))
+ .run(
+ parameters.getRuntime(),
+ Executor.class,
+ Boolean.toString(parameters.getRuntime().isCf()))
+ .assertSuccess()
+ .getStdOut();
+ }
+ assertLines2By2Correct(stdout);
+ }
+
+ @Test
public void testRetargetOverrideD8() throws Exception {
+ Assume.assumeTrue(parameters.getRuntime().isDex());
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
String stdout =
testForD8()
@@ -52,7 +113,10 @@
parameters.getApiLevel(),
keepRuleConsumer.get(),
shrinkDesugaredLibrary)
- .run(parameters.getRuntime(), Executor.class)
+ .run(
+ parameters.getRuntime(),
+ Executor.class,
+ Boolean.toString(parameters.getRuntime().isCf()))
.assertSuccess()
.getStdOut();
assertLines2By2Correct(stdout);
@@ -60,6 +124,7 @@
@Test
public void testRetargetOverrideR8() throws Exception {
+ Assume.assumeTrue(parameters.getRuntime().isDex());
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
String stdout =
testForR8(Backend.DEX)
@@ -74,7 +139,10 @@
parameters.getApiLevel(),
keepRuleConsumer.get(),
shrinkDesugaredLibrary)
- .run(parameters.getRuntime(), Executor.class)
+ .run(
+ parameters.getRuntime(),
+ Executor.class,
+ Boolean.toString(parameters.getRuntime().isCf()))
.assertSuccess()
.getStdOut();
assertLines2By2Correct(stdout);
@@ -83,27 +151,32 @@
static class Executor {
public static void main(String[] args) {
- directTypes();
- polyTypes();
- baseTypes();
+ boolean isJvm = Boolean.parseBoolean(args[0]);
+ directTypes(isJvm);
+ polyTypes(isJvm);
+ baseTypes(isJvm);
}
- public static void directTypes() {
+ public static void directTypes(boolean isJvm) {
MyCalendarOverride myCal = new MyCalendarOverride(1990, 2, 22);
- System.out.println(myCal.toZonedDateTime());
- System.out.println("1990-11-22T00:00Z[GMT]");
- System.out.println(myCal.toInstant());
- System.out.println("1990-03-22T00:00:00Z");
+ if (!isJvm) {
+ System.out.println(myCal.toZonedDateTime());
+ System.out.println("1990-11-22T00:00Z[GMT]");
+ System.out.println(myCal.toInstant());
+ System.out.println("1990-03-22T00:00:00Z");
+ }
MyCalendarNoOverride myCalN = new MyCalendarNoOverride(1990, 2, 22);
- System.out.println(myCalN.toZonedDateTime());
- System.out.println("1990-03-22T00:00Z[GMT]");
- System.out.println(myCalN.superToZonedDateTime());
- System.out.println("1990-03-22T00:00Z[GMT]");
- System.out.println(myCalN.toInstant());
- System.out.println("1990-03-22T00:00:00Z");
- System.out.println(myCalN.superToInstant());
- System.out.println("1990-03-22T00:00:00Z");
+ if (!isJvm) {
+ System.out.println(myCalN.toZonedDateTime());
+ System.out.println("1990-03-22T00:00Z[GMT]");
+ System.out.println(myCalN.superToZonedDateTime());
+ System.out.println("1990-03-22T00:00Z[GMT]");
+ System.out.println(myCalN.toInstant());
+ System.out.println("1990-03-22T00:00:00Z");
+ System.out.println(myCalN.superToInstant());
+ System.out.println("1990-03-22T00:00:00Z");
+ }
MyDateDoubleOverride myDateCast2 = new MyDateDoubleOverride(123456789);
System.out.println(myDateCast2.toInstant());
@@ -128,12 +201,13 @@
System.out.println("145");
Date date1 = MyDateNoOverride.from(myCal.toInstant());
- System.out.println(date1.toInstant());
- System.out.println("1990-03-22T00:00:00Z");
- Date date2 = MyDateOverride.from(myCal.toInstant());
- System.out.println(date2.toInstant());
- System.out.println("1990-03-22T00:00:00Z");
-
+ if (!isJvm) {
+ System.out.println(date1.toInstant());
+ System.out.println("1990-03-22T00:00:00Z");
+ Date date2 = MyDateOverride.from(myCal.toInstant());
+ System.out.println(date2.toInstant());
+ System.out.println("1990-03-22T00:00:00Z");
+ }
System.out.println(MyDateDoubleOverride.from(myCal.toInstant()).toInstant());
System.out.println("1970-01-02T10:17:36.788Z");
@@ -141,7 +215,7 @@
System.out.println("1970-01-02T10:17:36.788Z");
}
- public static void polyTypes() {
+ public static void polyTypes(boolean isJvm) {
Date myDateCast = new MyDateOverride(123456789);
System.out.println(myDateCast.toInstant());
System.out.println("1970-01-02T10:17:45.789Z");
@@ -155,27 +229,35 @@
System.out.println("1970-01-02T10:17:36.789Z");
GregorianCalendar myCalCast = new MyCalendarOverride(1990, 2, 22);
- System.out.println(myCalCast.toZonedDateTime());
- System.out.println("1990-11-22T00:00Z[GMT]");
- System.out.println(myCalCast.toInstant());
- System.out.println("1990-03-22T00:00:00Z");
+ if (!isJvm) {
+ System.out.println(myCalCast.toZonedDateTime());
+ System.out.println("1990-11-22T00:00Z[GMT]");
+ System.out.println(myCalCast.toInstant());
+ System.out.println("1990-03-22T00:00:00Z");
+ }
GregorianCalendar myCalN = new MyCalendarNoOverride(1990, 2, 22);
- System.out.println(myCalN.toZonedDateTime());
- System.out.println("1990-03-22T00:00Z[GMT]");
- System.out.println(myCalN.toInstant());
- System.out.println("1990-03-22T00:00:00Z");
+ if (!isJvm) {
+ System.out.println(myCalN.toZonedDateTime());
+ System.out.println("1990-03-22T00:00Z[GMT]");
+ System.out.println(myCalN.toInstant());
+ System.out.println("1990-03-22T00:00:00Z");
+ }
}
- public static void baseTypes() {
+ public static void baseTypes(boolean isJvm) {
java.sql.Date date = new java.sql.Date(123456789);
- // The following one is not working on JVMs, but works on Android...
- System.out.println(date.toInstant());
- System.out.println("1970-01-02T10:17:36.789Z");
+ if (!isJvm) {
+ // The following one is not working on JVMs, but works on Android...
+ System.out.println(date.toInstant());
+ System.out.println("1970-01-02T10:17:36.789Z");
+ }
GregorianCalendar gregCal = new GregorianCalendar(1990, 2, 22);
- System.out.println(gregCal.toInstant());
- System.out.println("1990-03-22T00:00:00Z");
+ if (!isJvm) {
+ System.out.println(gregCal.toInstant());
+ System.out.println("1990-03-22T00:00:00Z");
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java b/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java
index 9f9dd85..3f0992a 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java
@@ -34,10 +34,10 @@
@Parameterized.Parameters(name = "{0}")
public static List<Object[]> data() {
- // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk15).
+ // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk16).
return buildParameters(
getTestParameters()
- .withCustomRuntime(CfRuntime.getCheckedInJdk15())
+ .withCustomRuntime(CfRuntime.getCheckedInJdk16())
.withDexRuntimes()
.withAllApiLevelsAlsoForCf()
.build());
diff --git a/src/test/java/com/android/tools/r8/desugar/records/InvalidRecordAttributeTest.java b/src/test/java/com/android/tools/r8/desugar/records/InvalidRecordAttributeTest.java
index e936a33..7064af6 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/InvalidRecordAttributeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/InvalidRecordAttributeTest.java
@@ -37,9 +37,9 @@
@Parameters(name = "{0} back: {1}")
public static List<Object[]> data() {
- // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk15).
+ // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk16).
return buildParameters(
- getTestParameters().withCustomRuntime(CfRuntime.getCheckedInJdk15()).build(),
+ getTestParameters().withCustomRuntime(CfRuntime.getCheckedInJdk16()).build(),
Backend.values());
}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java
index d388ac4..c6b3f68 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java
@@ -37,7 +37,7 @@
// TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk15).
return buildParameters(
getTestParameters()
- .withCustomRuntime(CfRuntime.getCheckedInJdk15())
+ .withCustomRuntime(CfRuntime.getCheckedInJdk16())
.withDexRuntimes()
.withAllApiLevelsAlsoForCf()
.build());
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java
index 711909f..55cb175 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java
@@ -47,10 +47,10 @@
@Parameterized.Parameters(name = "{0}")
public static List<Object[]> data() {
- // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk15).
+ // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk16).
return buildParameters(
getTestParameters()
- .withCustomRuntime(CfRuntime.getCheckedInJdk15())
+ .withCustomRuntime(CfRuntime.getCheckedInJdk16())
.withDexRuntimes()
.withAllApiLevelsAlsoForCf()
.build());
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java
index bd08a25..240e128 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java
@@ -40,10 +40,10 @@
@Parameterized.Parameters(name = "{0}")
public static List<Object[]> data() {
- // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk15).
+ // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk16).
return buildParameters(
getTestParameters()
- .withCustomRuntime(CfRuntime.getCheckedInJdk15())
+ .withCustomRuntime(CfRuntime.getCheckedInJdk16())
.withDexRuntimes()
.withAllApiLevelsAlsoForCf()
.build());
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java
index 15684fe..9663f43 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java
@@ -43,9 +43,9 @@
@Parameterized.Parameters(name = "{0}")
public static List<Object[]> data() {
- // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk15).
+ // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk16).
return buildParameters(
- getTestParameters().withCustomRuntime(CfRuntime.getCheckedInJdk15()).build());
+ getTestParameters().withCustomRuntime(CfRuntime.getCheckedInJdk16()).build());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java b/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java
index 7589764..86e30a6 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java
@@ -31,7 +31,7 @@
*/
public class RecordTestUtils {
- private static final String EXAMPLE_FOLDER = "examplesJava15";
+ private static final String EXAMPLE_FOLDER = "examplesJava16";
private static final String RECORD_FOLDER = "records";
public static Path jar() {
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java
index 5472f22..c36375e 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java
@@ -36,10 +36,10 @@
@Parameterized.Parameters(name = "{0}")
public static List<Object[]> data() {
- // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk15).
+ // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk16).
return buildParameters(
getTestParameters()
- .withCustomRuntime(CfRuntime.getCheckedInJdk15())
+ .withCustomRuntime(CfRuntime.getCheckedInJdk16())
.withDexRuntimes()
.withAllApiLevelsAlsoForCf()
.build());
diff --git a/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java b/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
index 4820c08..51596a1 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
@@ -35,10 +35,10 @@
@Parameterized.Parameters(name = "{0}")
public static List<Object[]> data() {
- // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk15).
+ // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk16).
return buildParameters(
getTestParameters()
- .withCustomRuntime(CfRuntime.getCheckedInJdk15())
+ .withCustomRuntime(CfRuntime.getCheckedInJdk16())
.withDexRuntimes()
.withAllApiLevelsAlsoForCf()
.build());
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java
index ee2e4e1..d311db8 100644
--- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java
@@ -13,7 +13,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime;
-import com.android.tools.r8.examples.jdk15.Sealed;
+import com.android.tools.r8.examples.jdk16.Sealed;
import com.android.tools.r8.utils.AndroidApiLevel;
import java.util.List;
import org.junit.Test;
@@ -28,9 +28,9 @@
@Parameters(name = "{0}")
public static List<Object[]> data() {
- // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk15).
+ // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk16).
return buildParameters(
- getTestParameters().withCustomRuntime(TestRuntime.getCheckedInJdk15()).build(),
+ getTestParameters().withCustomRuntime(TestRuntime.getCheckedInJdk16()).build(),
Backend.values());
}
@@ -44,7 +44,7 @@
testForJvm()
.addRunClasspathFiles(Sealed.jar())
.enablePreview()
- .run(TestRuntime.getCheckedInJdk15(), Sealed.Main.typeName())
+ .run(TestRuntime.getCheckedInJdk16(), Sealed.Main.typeName())
.assertSuccessWithOutputLines("R8 compiler", "D8 compiler");
}
diff --git a/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InlineIntoReprocessedStaticInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InlineIntoReprocessedStaticInterfaceMethodTest.java
new file mode 100644
index 0000000..fbc6474
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InlineIntoReprocessedStaticInterfaceMethodTest.java
@@ -0,0 +1,83 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.staticinterfacemethod;
+
+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 org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Reproduction for b/196496374. */
+@RunWith(Parameterized.class)
+public class InlineIntoReprocessedStaticInterfaceMethodTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection parameters() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A");
+ }
+
+ static class Main {
+
+ // Field where all writes are removed by the primary optimization pass. All methods that read
+ // this field will therefore be enqueued for reprocessing in the second optimization pass.
+ public static boolean neverWritten;
+
+ public static void main(String[] args) {
+ if (getFalse()) {
+ neverWritten = args.length > 0;
+ }
+ I.method();
+ }
+
+ static boolean getFalse() {
+ return false;
+ }
+ }
+
+ static class A {
+
+ static void greet() {
+ System.out.println("A");
+ }
+ }
+
+ interface I {
+
+ // Since this method is moved to I's companion class during the primary optimization pass, we
+ // will process the companion method in the second optimization pass, since the body references
+ // the `neverWritten` field.
+ @NeverInline
+ static void method() {
+ if (Main.neverWritten) {
+ // This will be inlined. When building IR for this method in the second optimization pass,
+ // we assert that the outermost caller position is the original signature of the enclosing
+ // method. This only holds if we have correctly recorded that the original signature of the
+ // companion method I$-CC.method() is I.method().
+ System.out.println("Dead...");
+ }
+ A.greet();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/examples/jdk16/PatternMatchingForInstenceof.java b/src/test/java/com/android/tools/r8/examples/jdk16/PatternMatchingForInstenceof.java
new file mode 100644
index 0000000..0cc4846
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/jdk16/PatternMatchingForInstenceof.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.examples.jdk16;
+
+import com.android.tools.r8.examples.JavaExampleClassProxy;
+import java.nio.file.Path;
+
+public class PatternMatchingForInstenceof {
+
+ private static final String EXAMPLE_FILE = "examplesJava16/pattern_matching_for_instanceof";
+
+ public static final JavaExampleClassProxy Main =
+ new JavaExampleClassProxy(EXAMPLE_FILE, "pattern_matching_for_instanceof/Main");
+
+ public static Path jar() {
+ return JavaExampleClassProxy.examplesJar(EXAMPLE_FILE);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/examples/jdk15/Records.java b/src/test/java/com/android/tools/r8/examples/jdk16/Records.java
similarity index 85%
rename from src/test/java/com/android/tools/r8/examples/jdk15/Records.java
rename to src/test/java/com/android/tools/r8/examples/jdk16/Records.java
index 9eafc56..a8677b8 100644
--- a/src/test/java/com/android/tools/r8/examples/jdk15/Records.java
+++ b/src/test/java/com/android/tools/r8/examples/jdk16/Records.java
@@ -2,14 +2,14 @@
// 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.examples.jdk15;
+package com.android.tools.r8.examples.jdk16;
import com.android.tools.r8.examples.JavaExampleClassProxy;
import java.nio.file.Path;
public class Records {
- private static final String EXAMPLE_FILE = "examplesJava15/records";
+ private static final String EXAMPLE_FILE = "examplesJava16/records";
public static final JavaExampleClassProxy Main =
new JavaExampleClassProxy(EXAMPLE_FILE, "records/Main");
diff --git a/src/test/java/com/android/tools/r8/examples/jdk15/Sealed.java b/src/test/java/com/android/tools/r8/examples/jdk16/Sealed.java
similarity index 89%
rename from src/test/java/com/android/tools/r8/examples/jdk15/Sealed.java
rename to src/test/java/com/android/tools/r8/examples/jdk16/Sealed.java
index 1450ee8..c995d13 100644
--- a/src/test/java/com/android/tools/r8/examples/jdk15/Sealed.java
+++ b/src/test/java/com/android/tools/r8/examples/jdk16/Sealed.java
@@ -2,14 +2,14 @@
// 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.examples.jdk15;
+package com.android.tools.r8.examples.jdk16;
import com.android.tools.r8.examples.JavaExampleClassProxy;
import java.nio.file.Path;
public class Sealed {
- private static final String EXAMPLE_FILE = "examplesJava15/sealed";
+ private static final String EXAMPLE_FILE = "examplesJava16/sealed";
public static final JavaExampleClassProxy Compiler =
new JavaExampleClassProxy(EXAMPLE_FILE, "sealed/Compiler");
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePartialTypeArgumentApplierTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePartialTypeArgumentApplierTest.java
index a040b37..da89df6 100644
--- a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePartialTypeArgumentApplierTest.java
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePartialTypeArgumentApplierTest.java
@@ -123,7 +123,7 @@
HashMap::new,
s -> s,
ClassTypeSignature::new,
- (val1, val2) -> {
+ (key, val1, val2) -> {
throw new Unreachable("No keys should be merged");
}))
.addLiveParameters(liveVariables),
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
index 57078d1..8538449 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
@@ -22,7 +22,7 @@
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.LookupResult;
import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.AndroidApp;
@@ -75,7 +75,7 @@
appInfo().resolveMethodOnClass(method.getReference(), id.holder).getSingleTarget(), method);
// Check lookup targets with include method.
- ResolutionResult resolutionResult =
+ MethodResolutionResult resolutionResult =
appInfo().resolveMethodOnClass(method.getReference(), clazz);
AppInfoWithLiveness appInfo = null; // TODO(b/154881041): Remove or compute liveness.
LookupResult lookupResult =
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
index 5dd3140..5162100 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
@@ -61,15 +62,14 @@
public void testWriteOnlyField_dontoptimize() throws Exception {
CodeInspector inspector = runR8(DONT_OPTIMIZE);
ClassSubject clazz = inspector.clazz(QUALIFIED_CLASS_NAME);
- clazz.forAllMethods(
- methodSubject -> {
- // Dead code removal is not part of -dontoptimize. That is, even with -dontoptimize,
- // field put instructions are gone with better dead code removal.
- assertTrue(
- methodSubject
- .streamInstructions()
- .noneMatch(i -> i.isInstancePut() || i.isStaticPut()));
- });
+ // With the support of 'allowshrinking' dontoptimize will effectivelys pin all
+ // items that are not tree shaken out. The field operations will thus remain.
+ assertTrue(clazz.clinit().streamInstructions().anyMatch(InstructionSubject::isStaticPut));
+ assertTrue(
+ clazz
+ .uniqueInstanceInitializer()
+ .streamInstructions()
+ .anyMatch(InstructionSubject::isInstancePut));
}
private CodeInspector runR8(Path proguardConfig) throws Exception {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationLibraryLambdaPropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationLibraryLambdaPropagationTest.java
index 774267d..d53a470 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationLibraryLambdaPropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationLibraryLambdaPropagationTest.java
@@ -8,8 +8,9 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
import java.util.function.Consumer;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -19,18 +20,23 @@
@RunWith(Parameterized.class)
public class CallSiteOptimizationLibraryLambdaPropagationTest extends TestBase {
+ private final boolean enableExperimentalArgumentPropagation;
private final TestParameters parameters;
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters()
- .withCfRuntimes()
- .withDexRuntimes()
- .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
- .build();
+ @Parameters(name = "{1}, experimental: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(),
+ getTestParameters()
+ .withCfRuntimes()
+ .withDexRuntimes()
+ .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
+ .build());
}
- public CallSiteOptimizationLibraryLambdaPropagationTest(TestParameters parameters) {
+ public CallSiteOptimizationLibraryLambdaPropagationTest(
+ boolean enableExperimentalArgumentPropagation, TestParameters parameters) {
+ this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation;
this.parameters = parameters;
}
@@ -39,6 +45,14 @@
testForR8(parameters.getBackend())
.addInnerClasses(CallSiteOptimizationLibraryLambdaPropagationTest.class)
.addKeepMainRule(TestClass.class)
+ .applyIf(
+ enableExperimentalArgumentPropagation,
+ builder ->
+ builder.addOptionsModification(
+ options ->
+ options
+ .callSiteOptimizationOptions()
+ .setEnableExperimentalArgumentPropagation()))
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationPinnedMethodOverridePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationPinnedMethodOverridePropagationTest.java
index 9e42eb6..2bf8f8d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationPinnedMethodOverridePropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationPinnedMethodOverridePropagationTest.java
@@ -11,9 +11,9 @@
import com.android.tools.r8.R8TestCompileResult;
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.CodeInspector;
+import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -24,14 +24,19 @@
private static final String CLASS_PREFIX =
"com.android.tools.r8.ir.optimize.callsites.CallSiteOptimizationPinnedMethodOverridePropagationTest$";
+
+ private final boolean enableExperimentalArgumentPropagation;
private final TestParameters parameters;
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ @Parameters(name = "{1}, experimental: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
}
- public CallSiteOptimizationPinnedMethodOverridePropagationTest(TestParameters parameters) {
+ public CallSiteOptimizationPinnedMethodOverridePropagationTest(
+ boolean enableExperimentalArgumentPropagation, TestParameters parameters) {
+ this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation;
this.parameters = parameters;
}
@@ -58,13 +63,20 @@
+ "Arg getArg2(); \npublic static "
+ CLASS_PREFIX
+ "Call getCaller(); \n}"))
+ .applyIf(
+ enableExperimentalArgumentPropagation,
+ builder ->
+ builder.addOptionsModification(
+ options ->
+ options
+ .callSiteOptimizationOptions()
+ .setEnableExperimentalArgumentPropagation()))
.enableNoVerticalClassMergingAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.enableInliningAnnotations()
.enableMemberValuePropagationAnnotations()
.setMinApi(parameters.getApiLevel())
.compile();
- CodeInspector inspector = compiled.inspector();
compiled.run(parameters.getRuntime(), Main2.class).assertSuccessWithOutputLines("Arg1");
testForD8()
.addProgramClasses(Main.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationProgramLambdaPropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationProgramLambdaPropagationTest.java
index 86145c6..6d8e9d4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationProgramLambdaPropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationProgramLambdaPropagationTest.java
@@ -8,7 +8,8 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -17,14 +18,18 @@
@RunWith(Parameterized.class)
public class CallSiteOptimizationProgramLambdaPropagationTest extends TestBase {
+ private final boolean enableExperimentalArgumentPropagation;
private final TestParameters parameters;
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
+ @Parameters(name = "{1}, experimental: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
}
- public CallSiteOptimizationProgramLambdaPropagationTest(TestParameters parameters) {
+ public CallSiteOptimizationProgramLambdaPropagationTest(
+ boolean enableExperimentalArgumentPropagation, TestParameters parameters) {
+ this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation;
this.parameters = parameters;
}
@@ -33,6 +38,14 @@
testForR8(parameters.getBackend())
.addInnerClasses(CallSiteOptimizationProgramLambdaPropagationTest.class)
.addKeepMainRule(TestClass.class)
+ .applyIf(
+ enableExperimentalArgumentPropagation,
+ builder ->
+ builder.addOptionsModification(
+ options ->
+ options
+ .callSiteOptimizationOptions()
+ .setEnableExperimentalArgumentPropagation()))
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationWithInvokeCustomTargetTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationWithInvokeCustomTargetTest.java
index 1a2d667..d0d2d22 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationWithInvokeCustomTargetTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationWithInvokeCustomTargetTest.java
@@ -12,7 +12,7 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.google.common.collect.ImmutableList;
@@ -33,18 +33,23 @@
private static final String EXPECTED = StringUtils.lines("Hello world!");
+ private final boolean enableExperimentalArgumentPropagation;
private final TestParameters parameters;
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters()
- .withAllRuntimes()
- // Only works when invoke-custom/dynamic are supported and ConstantCallSite defined.
- .withApiLevelsStartingAtIncluding(apiLevelWithInvokeCustomSupport())
- .build();
+ @Parameters(name = "{1}, experimental: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(),
+ getTestParameters()
+ .withAllRuntimes()
+ // Only works when invoke-custom/dynamic are supported and ConstantCallSite defined.
+ .withApiLevelsStartingAtIncluding(apiLevelWithInvokeCustomSupport())
+ .build());
}
- public CallSiteOptimizationWithInvokeCustomTargetTest(TestParameters parameters) {
+ public CallSiteOptimizationWithInvokeCustomTargetTest(
+ boolean enableExperimentalArgumentPropagation, TestParameters parameters) {
+ this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation;
this.parameters = parameters;
}
@@ -61,9 +66,17 @@
testForR8(parameters.getBackend())
.addProgramClassFileData(getProgramClassFileData())
.addKeepMainRule(TestClass.class)
- .setMinApi(parameters.getApiLevel())
.addKeepMethodRules(methodFromMethod(TestClass.class.getDeclaredMethod("bar", int.class)))
+ .applyIf(
+ enableExperimentalArgumentPropagation,
+ builder ->
+ builder.addOptionsModification(
+ options ->
+ options
+ .callSiteOptimizationOptions()
+ .setEnableExperimentalArgumentPropagation()))
.enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED)
.inspect(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationWithLambdaTargetTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationWithLambdaTargetTest.java
index c04f9b6..4db8c2f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationWithLambdaTargetTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationWithLambdaTargetTest.java
@@ -8,7 +8,8 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -17,14 +18,18 @@
@RunWith(Parameterized.class)
public class CallSiteOptimizationWithLambdaTargetTest extends TestBase {
+ private final boolean enableExperimentalArgumentPropagation;
private final TestParameters parameters;
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
+ @Parameters(name = "{1}, experimental: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
}
- public CallSiteOptimizationWithLambdaTargetTest(TestParameters parameters) {
+ public CallSiteOptimizationWithLambdaTargetTest(
+ boolean enableExperimentalArgumentPropagation, TestParameters parameters) {
+ this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation;
this.parameters = parameters;
}
@@ -33,6 +38,14 @@
testForR8(parameters.getBackend())
.addInnerClasses(CallSiteOptimizationWithLambdaTargetTest.class)
.addKeepMainRule(TestClass.class)
+ .applyIf(
+ enableExperimentalArgumentPropagation,
+ builder ->
+ builder.addOptionsModification(
+ options ->
+ options
+ .callSiteOptimizationOptions()
+ .setEnableExperimentalArgumentPropagation()))
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/KeptMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/KeptMethodTest.java
index 8d405c4..b579686 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/KeptMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/KeptMethodTest.java
@@ -11,29 +11,34 @@
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.graph.ProgramMethod;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.lang.reflect.Method;
+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 KeptMethodTest extends TestBase {
private static final Class<?> MAIN = Main.class;
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
+ @Parameters(name = "{1}, experimental: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
}
+ private final boolean enableExperimentalArgumentPropagation;
private final TestParameters parameters;
- public KeptMethodTest(TestParameters parameters) {
+ public KeptMethodTest(boolean enableExperimentalArgumentPropagation, TestParameters parameters) {
+ this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation;
this.parameters = parameters;
}
@@ -43,12 +48,19 @@
.addInnerClasses(KeptMethodTest.class)
.addKeepMainRule(MAIN)
.addKeepClassAndMembersRules(A.class)
+ .applyIf(
+ enableExperimentalArgumentPropagation,
+ builder ->
+ builder.addOptionsModification(
+ options ->
+ options
+ .callSiteOptimizationOptions()
+ .setEnableExperimentalArgumentPropagation()))
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.addOptionsModification(
- o -> {
- o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect;
- })
+ o ->
+ o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("non-null", "non-null")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/LibraryMethodOverridesTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/LibraryMethodOverridesTest.java
index 8a057dd..ea7641f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/LibraryMethodOverridesTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/LibraryMethodOverridesTest.java
@@ -11,13 +11,14 @@
import com.android.tools.r8.R8TestCompileResult;
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 com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.List;
import java.util.function.Predicate;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -27,18 +28,23 @@
public class LibraryMethodOverridesTest extends TestBase {
private static final Class<?> MAIN = TestClass.class;
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters()
- .withCfRuntimes()
- // java.util.function.Predicate is not available prior to API level 24 (V7.0).
- .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
- .build();
+ @Parameterized.Parameters(name = "{1}, experimental: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(),
+ getTestParameters()
+ .withCfRuntimes()
+ // java.util.function.Predicate is not available prior to API level 24 (V7.0).
+ .withDexRuntimesStartingFromIncluding(Version.V7_0_0)
+ .build());
}
+ private final boolean enableExperimentalArgumentPropagation;
private final TestParameters parameters;
- public LibraryMethodOverridesTest(TestParameters parameters) {
+ public LibraryMethodOverridesTest(
+ boolean enableExperimentalArgumentPropagation, TestParameters parameters) {
+ this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation;
this.parameters = parameters;
}
@@ -54,10 +60,18 @@
.addProgramClasses(TestClass.class, CustomPredicate.class)
.addClasspathClasses(LibClass.class)
.addKeepMainRule(MAIN)
+ .addOptionsModification(
+ o ->
+ o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect)
+ .applyIf(
+ enableExperimentalArgumentPropagation,
+ builder ->
+ builder.addOptionsModification(
+ options ->
+ options
+ .callSiteOptimizationOptions()
+ .setEnableExperimentalArgumentPropagation()))
.enableInliningAnnotations()
- .addOptionsModification(o -> {
- o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect;
- })
.setMinApi(parameters.getRuntime())
.compile()
.addRunClasspathFiles(libraryCompileResult.writeToZip())
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/InstanceOfSpecializedMethodClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/InstanceOfSpecializedMethodClassInlinerTest.java
new file mode 100644
index 0000000..125f3cd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/InstanceOfSpecializedMethodClassInlinerTest.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.classinliner;
+
+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 org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Regression test for b/195234829. */
+@RunWith(Parameterized.class)
+public class InstanceOfSpecializedMethodClassInlinerTest extends TestBase {
+
+ @Parameter public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection setup() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("false", "true");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(new A().isB());
+ System.out.println(new B().isB());
+ }
+ }
+
+ static class A {
+
+ @NeverInline
+ boolean isB() {
+ return false;
+ }
+ }
+
+ static class B extends A {
+
+ @NeverInline
+ @Override
+ boolean isB() {
+ return true;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/library/ObjectsRequireNonNullElseGetTest.java b/src/test/java/com/android/tools/r8/ir/optimize/library/ObjectsRequireNonNullElseGetTest.java
index e0a4c6d..aadd5dd 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/library/ObjectsRequireNonNullElseGetTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/library/ObjectsRequireNonNullElseGetTest.java
@@ -41,7 +41,17 @@
}
@Test
- public void test() throws Exception {
+ public void testD8() throws Exception {
+ testForD8(parameters.getBackend())
+ .addProgramClassFileData(getProgramClassFileData())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Foo", "Bar", "Expected NPE");
+ }
+
+ @Test
+ public void testR8() throws Exception {
testForR8(parameters.getBackend())
.addProgramClassFileData(getProgramClassFileData())
.addKeepMainRule(Main.class)
@@ -65,10 +75,12 @@
assertThat(testNullArgumentMethodSubject, isPresent());
assertThat(
testNullArgumentMethodSubject,
- not(invokesMethodWithName("requireNonNullElseGet")));
+ parameters.getApiLevel().isGreaterThan(AndroidApiLevel.Q)
+ ? invokesMethodWithName("requireNonNullElseGet")
+ : not(invokesMethodWithName("requireNonNullElseGet")));
})
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("Foo", "Bar");
+ .assertSuccessWithOutputLines("Foo", "Bar", "Expected NPE");
}
private byte[] getProgramClassFileData() throws IOException {
@@ -83,6 +95,12 @@
public static void main(String[] args) {
testNonNullArgument();
testNullArgument();
+ try {
+ testNullArgumentAndNullSupplier();
+ System.out.println("Unexpected");
+ } catch (NullPointerException e) {
+ System.out.println("Expected NPE");
+ }
}
@NeverInline
@@ -94,6 +112,11 @@
static void testNullArgument() {
System.out.println(Mock.requireNonNullElseGet(null, () -> "Bar"));
}
+
+ @NeverInline
+ static void testNullArgumentAndNullSupplier() {
+ System.out.println(Mock.requireNonNullElseGet(null, () -> null));
+ }
}
// References to this class are rewritten to java.util.Objects by transformation.
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromDefaultInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromDefaultInterfaceMethodTest.java
new file mode 100644
index 0000000..9b3dd42
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromDefaultInterfaceMethodTest.java
@@ -0,0 +1,129 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.outliner;
+
+import static com.android.tools.r8.references.Reference.methodFromMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+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.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+// Regression test for the issue causing extra bookkeeping in outlining of interface methods.
+// See clean-up issue b/167345026.
+@RunWith(Parameterized.class)
+public class OutlineFromDefaultInterfaceMethodTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public OutlineFromDefaultInterfaceMethodTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addKeepMethodRules(methodFromMethod(TestClass.class.getDeclaredMethod("getI")))
+ .addOptionsModification(
+ options -> {
+ options.outline.threshold = 2;
+ options.outline.minSize = 2;
+ })
+ .addHorizontallyMergedClassesInspector(
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .noHorizontalClassMergingOfSynthetics()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!", "Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject interfaceSubject;
+ MethodSubject greetMethodSubject;
+ if (parameters.isCfRuntime()
+ || parameters
+ .getApiLevel()
+ .isGreaterThanOrEqualTo(apiLevelWithDefaultInterfaceMethodsSupport())) {
+ interfaceSubject = inspector.clazz(I.class);
+ greetMethodSubject = interfaceSubject.uniqueMethodWithName("greet");
+ } else {
+ interfaceSubject = inspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(I.class));
+ List<FoundMethodSubject> companionMethods = interfaceSubject.allMethods();
+ assertEquals(1, companionMethods.size());
+ greetMethodSubject = companionMethods.get(0);
+ }
+ assertThat(interfaceSubject, isPresent());
+ assertThat(greetMethodSubject, isPresent());
+ assertEquals(
+ 1,
+ greetMethodSubject.streamInstructions().filter(InstructionSubject::isInvokeStatic).count());
+ }
+
+ static class TestClass {
+
+ static I getI() {
+ return new I() {};
+ }
+
+ public static void main(String... args) {
+ greet();
+ getI().greet();
+ }
+
+ @NeverInline
+ static void greet() {
+ Greeter.hello();
+ Greeter.world();
+ }
+ }
+
+ interface I {
+
+ @NeverInline
+ default void greet() {
+ Greeter.hello();
+ Greeter.world();
+ }
+ }
+
+ @NeverClassInline
+ public static class Greeter {
+
+ @NeverInline
+ public static void hello() {
+ System.out.print("Hello");
+ }
+
+ @NeverInline
+ public static void world() {
+ System.out.println(" world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/java_language/pattern_matching_for_instenceof/PattternMatchingForInstanceOfTest.java b/src/test/java/com/android/tools/r8/java_language/pattern_matching_for_instenceof/PattternMatchingForInstanceOfTest.java
new file mode 100644
index 0000000..345f198
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/java_language/pattern_matching_for_instenceof/PattternMatchingForInstanceOfTest.java
@@ -0,0 +1,79 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.java_language.pattern_matching_for_instenceof;
+
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.examples.jdk16.PatternMatchingForInstenceof;
+import com.android.tools.r8.utils.InternalOptions.TestingOptions;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class PattternMatchingForInstanceOfTest extends TestBase {
+
+ @Parameter public TestParameters parameters;
+
+ private static List<String> EXPECTED = ImmutableList.of("Hello, world!");
+
+ private static final Path JAR = PatternMatchingForInstenceof.jar();
+ private static final String MAIN = PatternMatchingForInstenceof.Main.typeName();
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> data() {
+ // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk16).
+ return buildParameters(
+ getTestParameters()
+ .withCustomRuntime(CfRuntime.getCheckedInJdk16())
+ .withDexRuntimes()
+ .withAllApiLevelsAlsoForCf()
+ .build());
+ }
+
+ @Test
+ public void testD8AndJvm() throws Exception {
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addRunClasspathFiles(JAR)
+ .enablePreview()
+ .run(parameters.getRuntime(), MAIN)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+ testForD8(parameters.getBackend())
+ .addProgramFiles(JAR)
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
+ .compile()
+ .run(parameters.getRuntime(), MAIN)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ R8TestBuilder<?> builder =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(JAR)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(MAIN)
+ .addOptionsModification(TestingOptions::allowExperimentClassFileVersion);
+ if (parameters.getBackend().isDex()) {
+ builder.run(parameters.getRuntime(), MAIN).assertSuccessWithOutputLines(EXPECTED);
+ } else {
+ testForJvm()
+ .addRunClasspathFiles(builder.compile().writeToZip())
+ .enablePreview()
+ .run(parameters.getRuntime(), MAIN)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParserTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParserTest.java
index 336e9a8..6ba334d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParserTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParserTest.java
@@ -53,9 +53,9 @@
"*E");
Result result = KotlinSourceDebugExtensionParser.parse(annotationData);
assertNotNull(result);
- assertEquals(1, result.size());
- assertEquals(1, (int) result.lookup(1).getKey());
- Position position = result.lookup(1).getValue();
+ assertEquals(1, result.inlinePositionsCount());
+ assertEquals(1, (int) result.lookupInlinedPosition(1).getKey());
+ Position position = result.lookupInlinedPosition(1).getValue();
assertEquals("EnumSwitch.kt", position.getSource().getFileName());
assertEquals("enumswitch/EnumSwitchKt", position.getSource().getPath());
assertEquals(1, position.getRange().from);
@@ -94,21 +94,21 @@
"*E");
Result result = KotlinSourceDebugExtensionParser.parse(annotationData);
assertNotNull(result);
- assertEquals(3, result.size());
- assertEquals(1, (int) result.lookup(1).getKey());
- assertEquals(23, (int) result.lookup(23).getKey());
- assertEquals(24, (int) result.lookup(24).getKey());
+ assertEquals(3, result.inlinePositionsCount());
+ assertEquals(1, (int) result.lookupInlinedPosition(1).getKey());
+ assertEquals(23, (int) result.lookupInlinedPosition(23).getKey());
+ assertEquals(24, (int) result.lookupInlinedPosition(24).getKey());
// Check that files are correctly parsed.
- Position pos1 = result.lookup(1).getValue();
+ Position pos1 = result.lookupInlinedPosition(1).getValue();
assertEquals("Main.kt", pos1.getSource().getFileName());
assertEquals("retrace/MainKt", pos1.getSource().getPath());
- Position pos2 = result.lookup(23).getValue();
+ Position pos2 = result.lookupInlinedPosition(23).getValue();
assertEquals("InlineFunction.kt", pos2.getSource().getFileName());
assertEquals("retrace/InlineFunctionKt", pos2.getSource().getPath());
- Position pos3 = result.lookup(24).getValue();
+ Position pos3 = result.lookupInlinedPosition(24).getValue();
assertEquals("InlineFunction.kt", pos3.getSource().getFileName());
assertEquals("retrace/InlineFunction", pos3.getSource().getPath());
@@ -315,8 +315,8 @@
"*E");
Result parsedResult = KotlinSourceDebugExtensionParser.parse(annotationData);
assertNotNull(parsedResult);
- assertEquals(24, (int) parsedResult.lookup(25).getKey());
- Position value = parsedResult.lookup(25).getValue();
+ assertEquals(24, (int) parsedResult.lookupInlinedPosition(25).getKey());
+ Position value = parsedResult.lookupInlinedPosition(25).getValue();
assertEquals(12, value.getRange().from);
assertEquals(13, value.getRange().to);
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
index f8dcce6..baf1308 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.ThrowableConsumer;
@@ -36,7 +37,14 @@
this.parameters = parameters;
}
- private void test(ThrowableConsumer<R8FullTestBuilder> consumer) throws Exception {
+ private void test(ThrowableConsumer<R8FullTestBuilder> testBuilderConsumer) throws Exception {
+ test(testBuilderConsumer, ThrowableConsumer.empty());
+ }
+
+ private void test(
+ ThrowableConsumer<R8FullTestBuilder> testBuilderConsumer,
+ ThrowableConsumer<R8TestCompileResult> compileResultBuilder)
+ throws Exception {
testForR8(parameters.getBackend())
.addLibraryFiles(
ToolHelper.getMostRecentAndroidJar(),
@@ -48,8 +56,9 @@
.addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
.allowUnusedDontWarnKotlinReflectJvmInternal(kotlinc.isNot(KOTLINC_1_3_72))
.allowUnusedProguardConfigurationRules(kotlinc.isNot(KOTLINC_1_3_72))
- .apply(consumer)
+ .apply(testBuilderConsumer)
.compile()
+ .apply(compileResultBuilder)
.apply(assertUnusedKeepRuleForKotlinMetadata(kotlinc.isNot(KOTLINC_1_3_72)));
}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodDirectRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodDirectRetraceTest.java
index e505e23..b0a87ee 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodDirectRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodDirectRetraceTest.java
@@ -48,19 +48,13 @@
@Override
public void configure(R8TestBuilder<?> builder) {
builder.enableInliningAnnotations();
+ builder.enableExperimentalMapFileVersion();
}
@Test
public void testSourceFileAndLineNumberTable() throws Exception {
runTest(
ImmutableList.of("-keepattributes SourceFile,LineNumberTable"),
- // For the desugaring to companion classes the retrace stacktrace is still the same
- // as the mapping file has a fully qualified class name in the method mapping, e.g.:
- //
- // com.android.tools.r8.naming.retrace.InterfaceWithStaticMethod$-CC
- // -> com.android.tools.r8.naming.retrace.a:1:1:void
- // com.android.tools.r8.naming.retrace.InterfaceWithStaticMethod$.staticMethod():80:80
- // -> a
(StackTrace actualStackTrace, StackTrace retracedStackTrace) ->
assertThat(retracedStackTrace, isSameExceptForFileName(expectedStackTrace)));
}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodsInlineIntoStaticRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodsInlineIntoStaticRetraceTest.java
index 7904ede..38ef3038 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodsInlineIntoStaticRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodsInlineIntoStaticRetraceTest.java
@@ -38,6 +38,7 @@
@Override
public void configure(R8TestBuilder<?> builder) {
builder.enableInliningAnnotations();
+ builder.enableExperimentalMapFileVersion();
}
@Override
@@ -55,13 +56,9 @@
public void testSourceFileAndLineNumberTable() throws Exception {
runTest(
ImmutableList.of("-keepattributes SourceFile,LineNumberTable"),
- // For the desugaring to companion classes the retrace stacktrace is still the same
- // as the mapping file has a fully qualified class name in the method mapping, e.g.:
- //
- // com.android.tools.r8.naming.retrace.InterfaceWithDefaultMethod1$-CC
- // -> com.android.tools.r8.naming.retrace.a:1:1:void
- // com.android.tools.r8.naming.retrace.InterfaceWithDefaultMethod1.defaultMethod1():80:80
- // -> a
+ // Companion methods are treated as having inlined the interface method code.
+ // If compiling with synthetic marking support in the mapping file, the synthetic frames
+ // are removed and the trace will be equal to RI.
(StackTrace actualStackTrace, StackTrace retracedStackTrace) ->
assertThat(retracedStackTrace, isSameExceptForFileName(expectedStackTrace)));
}
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarStaticInterfaceMethodsRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarStaticInterfaceMethodsRetraceTest.java
index 04f722d..599fa3b 100644
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarStaticInterfaceMethodsRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarStaticInterfaceMethodsRetraceTest.java
@@ -55,6 +55,10 @@
// TODO(b/186015503): This test fails when mapping via PCs.
// also the test should be updated to use TestParameters and api levels.
assumeTrue("b/186015503", !backend.isDex() || mode != CompilationMode.RELEASE);
+ // This also fails when desugaring due to the change in companion method stacks.
+ assumeTrue(
+ ToolHelper.getMinApiLevelForDexVm()
+ .isGreaterThanOrEqualTo(apiLevelWithDefaultInterfaceMethodsSupport()));
runTest(
ImmutableList.of("-keepattributes SourceFile,LineNumberTable"),
// For the desugaring to companion classes the retrace stacktrace is still the same
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentTest.java
new file mode 100644
index 0000000..4e44d1f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentTest.java
@@ -0,0 +1,94 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.InternalOptions.CallSiteOptimizationOptions;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class StaticMethodWithConstantArgumentTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection parameters() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> {
+ CallSiteOptimizationOptions callSiteOptimizationOptions =
+ options.callSiteOptimizationOptions();
+ callSiteOptimizationOptions.setEnableExperimentalArgumentPropagation();
+ callSiteOptimizationOptions.setEnableConstantPropagation();
+ })
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+
+ // The test() method has been optimized.
+ MethodSubject testMethodSubject = mainClassSubject.uniqueMethodWithName("test");
+ assertThat(testMethodSubject, isPresent());
+ // TODO(b/190154391): The parameter x should be removed.
+ assertEquals(1, testMethodSubject.getProgramMethod().getParameters().size());
+ assertTrue(
+ testMethodSubject.streamInstructions().noneMatch(InstructionSubject::isIf));
+
+ assertThat(mainClassSubject.uniqueMethodWithName("dead"), isAbsent());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello", "Hello", "Hello");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ test(42);
+ test(42);
+ test(42);
+ }
+
+ @NeverInline
+ static void test(int x) {
+ if (x == 42) {
+ System.out.println("Hello");
+ } else {
+ dead();
+ }
+ }
+
+ @NeverInline
+ static void dead() {
+ System.out.println("Unreachable");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentThroughCallChainTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentThroughCallChainTest.java
new file mode 100644
index 0000000..b0387b2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/StaticMethodWithConstantArgumentThroughCallChainTest.java
@@ -0,0 +1,125 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.InternalOptions.CallSiteOptimizationOptions;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class StaticMethodWithConstantArgumentThroughCallChainTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection parameters() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> {
+ CallSiteOptimizationOptions callSiteOptimizationOptions =
+ options.callSiteOptimizationOptions();
+ callSiteOptimizationOptions.setEnableExperimentalArgumentPropagation();
+ callSiteOptimizationOptions.setEnableConstantPropagation();
+ })
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+
+ // The test1(), test2(), and test3() methods have been optimized.
+ for (int i = 1; i <= 3; i++) {
+ MethodSubject testMethodSubject = mainClassSubject.uniqueMethodWithName("test" + i);
+ assertThat(testMethodSubject, isPresent());
+ // TODO(b/190154391): The parameter x should be removed.
+ assertEquals(1, testMethodSubject.getProgramMethod().getParameters().size());
+ assertTrue(
+ testMethodSubject.streamInstructions().noneMatch(InstructionSubject::isIf));
+ }
+
+ assertThat(mainClassSubject.uniqueMethodWithName("dead"), isAbsent());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(
+ "Hello from test1()",
+ "Hello from test2()",
+ "Hello from test3()",
+ "Hello from test1()",
+ "Hello from test2()",
+ "Hello from test3()",
+ "Hello from test1()",
+ "Hello from test2()",
+ "Hello from test3()");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ test1(42);
+ test1(42);
+ test1(42);
+ }
+
+ @NeverInline
+ static void test1(int x) {
+ if (x == 42) {
+ System.out.println("Hello from test1()");
+ } else {
+ dead();
+ }
+ test2(x);
+ }
+
+ @NeverInline
+ static void test2(int x) {
+ if (x == 42) {
+ System.out.println("Hello from test2()");
+ } else {
+ dead();
+ }
+ test3(x);
+ }
+
+ @NeverInline
+ static void test3(int x) {
+ if (x == 42) {
+ System.out.println("Hello from test3()");
+ } else {
+ dead();
+ }
+ }
+
+ @NeverInline
+ static void dead() {
+ System.out.println("Unreachable");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithSuffixRenamingConfigurationTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithSuffixRenamingConfigurationTest.java
deleted file mode 100644
index dcfd6c3..0000000
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithSuffixRenamingConfigurationTest.java
+++ /dev/null
@@ -1,80 +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.repackage;
-
-import static org.junit.Assert.assertEquals;
-
-import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.repackaging.Repackaging.SuffixRenamingRepackagingConfiguration;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class RepackageWithSuffixRenamingConfigurationTest extends RepackageTestBase {
-
- public RepackageWithSuffixRenamingConfigurationTest(
- String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
- super(flattenPackageHierarchyOrRepackageClasses, parameters);
- }
-
- @Test
- public void test() throws Exception {
- testForR8(parameters.getBackend())
- .addInnerClasses(getClass())
- .addKeepMainRule(TestClass.class)
- .addKeepClassRules(GreeterFoo.class)
- .addOptionsModification(
- options -> {
- options.testing.repackageWithNoMinification = true;
- options.testing.repackagingConfigurationFactory =
- appView ->
- new SuffixRenamingRepackagingConfiguration("Foo", appView.dexItemFactory());
- })
- .apply(this::configureRepackaging)
- .enableInliningAnnotations()
- .noMinification()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspect(this::inspect)
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutputLines("Hello world!");
- }
-
- private void inspect(CodeInspector inspector) {
- ClassSubject greeterSubject = inspector.clazz(Greeter.class);
- assertEquals(GreeterFoo.class.getTypeName() + "$1", greeterSubject.getFinalName());
-
- ClassSubject greeterFooSubject = inspector.clazz(GreeterFoo.class);
- assertEquals(GreeterFoo.class.getTypeName(), greeterFooSubject.getFinalName());
- }
-
- public static class TestClass {
-
- public static void main(String[] args) {
- Greeter.greet();
- GreeterFoo.greet();
- }
- }
-
- public static class Greeter extends Exception {
-
- @NeverInline
- public static void greet() {
- System.out.print("Hello");
- }
- }
-
- public static class GreeterFoo extends Exception {
-
- @NeverInline
- public static void greet() {
- System.out.println(" world!");
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java b/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java
index f1ea8f0..09e9882 100644
--- a/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java
@@ -21,7 +21,7 @@
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.DescriptorUtils;
import java.io.IOException;
@@ -59,7 +59,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(Base.class, "collect", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
assertTrue(resolutionResult.isSingleResolution());
DexProgramClass context =
appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
index 2b560e2..b176930 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
@@ -14,8 +14,8 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.LookupResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.resolution.singletarget.Main;
import com.android.tools.r8.resolution.singletarget.one.AbstractSubClass;
import com.android.tools.r8.resolution.singletarget.one.AbstractTopClass;
@@ -204,7 +204,7 @@
public void lookupVirtualTargets() {
DexMethod method = buildNullaryVoidMethod(invokeReceiver, methodName, appInfo.dexItemFactory());
Assert.assertNotNull(appInfo.resolveMethodOnClass(method).getSingleTarget());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
if (resolutionResult.isVirtualTarget()) {
LookupResult lookupResult =
resolutionResult.lookupVirtualDispatchTargets(
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodTest.java
index 4d4419f..473f349 100644
--- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodTest.java
@@ -14,7 +14,7 @@
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
@@ -91,7 +91,8 @@
@Test
public void resolveTarget() {
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(methodOnB, methodOnB.holder);
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOnClass(methodOnB, methodOnB.holder);
DexClass context = appInfo.definitionFor(methodOnB.holder);
assertTrue(resolutionResult.isIllegalAccessErrorResult(context, appInfo));
}
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodWithVirtualParentTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodWithVirtualParentTest.java
index 6be194c..cc2005d 100644
--- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodWithVirtualParentTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfPrivateStaticMethodWithVirtualParentTest.java
@@ -15,7 +15,7 @@
import com.android.tools.r8.TestRuntime;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableList;
import java.util.List;
@@ -107,7 +107,8 @@
@Test
public void testResolution() {
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(methodOnB, methodOnB.holder);
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOnClass(methodOnB, methodOnB.holder);
assertTrue(resolutionResult.isFailedResolution());
}
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java
index edc4c42..cbad874 100644
--- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java
@@ -16,8 +16,8 @@
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.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableList;
@@ -129,7 +129,7 @@
public void lookupSingleTarget() {
DexProgramClass bClass = appInfo.definitionForProgramType(methodOnBReference.holder);
ProgramMethod methodOnB = bClass.lookupProgramMethod(methodOnBReference);
- ResolutionResult resolutionResult =
+ MethodResolutionResult resolutionResult =
appInfo.resolveMethodOnInterface(methodOnBReference.holder, methodOnBReference);
DexEncodedMethod resolved = resolutionResult.getSingleTarget();
assertEquals(methodOnBReference, resolved.getReference());
@@ -141,7 +141,7 @@
@Test
public void lookupVirtualTargets() {
- ResolutionResult resolutionResult =
+ MethodResolutionResult resolutionResult =
appInfo.resolveMethodOnInterface(methodOnBReference.holder, methodOnBReference);
DexEncodedMethod resolved = resolutionResult.getSingleTarget();
assertEquals(methodOnBReference, resolved.getReference());
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java
index 7843995..c6e9206 100644
--- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java
@@ -18,7 +18,7 @@
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.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableList;
import java.util.List;
@@ -174,7 +174,8 @@
@Test
public void lookupSingleTarget() {
DexProgramClass bClass = appInfo.definitionForProgramType(methodOnB.holder);
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(methodOnB, methodOnB.holder);
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOnClass(methodOnB, methodOnB.holder);
DexEncodedMethod resolved = resolutionResult.getSingleTarget();
assertEquals(methodOnA, resolved.getReference());
assertFalse(resolutionResult.isVirtualTarget());
@@ -185,7 +186,8 @@
@Test
public void lookupVirtualTargets() {
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(methodOnB, methodOnB.holder);
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOnClass(methodOnB, methodOnB.holder);
DexEncodedMethod resolved = resolutionResult.getSingleTarget();
assertEquals(methodOnA, resolved.getReference());
assertFalse(resolutionResult.isVirtualTarget());
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
index 821bc4c..3803d1a 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
@@ -17,8 +17,8 @@
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.ResolutionResult;
-import com.android.tools.r8.graph.ResolutionResult.NoSuchMethodResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult.NoSuchMethodResult;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -121,7 +121,8 @@
// Resolve the method from the point of the declared holder.
assertEquals(method.holder, declaredClassDefinition.type);
- ResolutionResult resolutionResult = appInfo.resolveMethodOn(declaredClassDefinition, method);
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOn(declaredClassDefinition, method);
if (!symbolicReferenceIsDefiningType) {
// The targeted method is a private interface method and thus not a maximally specific method.
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java
index 83d363b..4787b97 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java
@@ -16,8 +16,8 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.ResolutionResult;
-import com.android.tools.r8.graph.ResolutionResult.NoSuchMethodResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult.NoSuchMethodResult;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.transformers.ClassFileTransformer;
@@ -119,7 +119,8 @@
// Resolve the method from the point of the declared holder.
assertEquals(method.holder, declaredClassDefinition.type);
- ResolutionResult resolutionResult = appInfo.resolveMethodOn(declaredClassDefinition, method);
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOn(declaredClassDefinition, method);
// The targeted method is a private interface method and thus not a maximally specific method.
assertTrue(resolutionResult instanceof NoSuchMethodResult);
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
index bec0f69..b14ab55 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessTest.java
@@ -17,7 +17,7 @@
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.utils.BooleanUtils;
@@ -95,7 +95,8 @@
// Resolve the method from the point of the declared holder.
assertEquals(method.holder, declaredClassDefinition.type);
- ResolutionResult resolutionResult = appInfo.resolveMethodOn(declaredClassDefinition, method);
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOn(declaredClassDefinition, method);
// Verify that the resolved method is on the defining class.
assertEquals(
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
index 56258b4..9c596c9 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodAccessWithIntermediateTest.java
@@ -17,7 +17,7 @@
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.utils.BooleanUtils;
@@ -119,7 +119,8 @@
// Resolve the method from the point of the declared holder.
assertEquals(method.holder, declaredClassDefinition.type);
- ResolutionResult resolutionResult = appInfo.resolveMethodOn(declaredClassDefinition, method);
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOn(declaredClassDefinition, method);
// Resolution fails when there is a mismatch between the symbolic reference and the definition.
if (!symbolicReferenceIsDefiningType) {
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
index b4dab4e..d65dabf 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialMethodPublicAccessWithIntermediateTest.java
@@ -15,7 +15,7 @@
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.transformers.ClassFileTransformer;
@@ -97,7 +97,8 @@
// Resolve the method from the point of the declared holder.
assertEquals(method.holder, declaredClassDefinition.type);
- ResolutionResult resolutionResult = appInfo.resolveMethodOn(declaredClassDefinition, method);
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOn(declaredClassDefinition, method);
// Verify that the resolved method is on the defining class.
assertEquals(
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java
index 9785c92..760215a 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java
@@ -13,7 +13,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.utils.BooleanUtils;
@@ -73,7 +73,7 @@
DexProgramClass bClass =
appInfo.definitionFor(buildType(B.class, appInfo.dexItemFactory())).asProgramClass();
DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(bar);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(bar);
assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appInfo));
}
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java
index 1df6b63..9a549f2 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessWithIntermediateClassTest.java
@@ -13,7 +13,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.utils.BooleanUtils;
@@ -72,7 +72,7 @@
DexProgramClass bClass =
appInfo.definitionFor(buildType(B.class, appInfo.dexItemFactory())).asProgramClass();
DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(bar);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(bar);
assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appInfo));
}
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java
index 7ab3821..5251469 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessTest.java
@@ -14,7 +14,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.utils.BooleanUtils;
@@ -76,7 +76,7 @@
DexProgramClass bClass =
appInfo.definitionFor(buildType(B.class, appInfo.dexItemFactory())).asProgramClass();
DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(bar);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(bar);
assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appInfo));
}
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java
index 2c0dbca..ec1222f 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestVirtualMethodAccessWithIntermediateClassTest.java
@@ -12,7 +12,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.utils.BooleanUtils;
@@ -71,7 +71,7 @@
DexProgramClass bClass =
appInfo.definitionFor(buildType(B.class, appInfo.dexItemFactory())).asProgramClass();
DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(bar);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(bar);
assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appInfo));
}
diff --git a/src/test/java/com/android/tools/r8/resolution/access/SelfVirtualMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/SelfVirtualMethodAccessTest.java
index cd2679e..ea12cad 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/SelfVirtualMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/SelfVirtualMethodAccessTest.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.StringUtils;
@@ -59,7 +59,7 @@
DexProgramClass aClass =
appInfo.definitionFor(buildType(A.class, appInfo.dexItemFactory())).asProgramClass();
DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(bar);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(bar);
assertEquals(OptionalBool.TRUE, resolutionResult.isAccessibleFrom(aClass, appInfo));
}
diff --git a/src/test/java/com/android/tools/r8/resolution/access/indirectmethod/IndirectMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/indirectmethod/IndirectMethodAccessTest.java
index 008a478..8341bba 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/indirectmethod/IndirectMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/indirectmethod/IndirectMethodAccessTest.java
@@ -12,7 +12,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.resolution.access.indirectmethod.pkg.C;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.OptionalBool;
@@ -65,7 +65,7 @@
DexProgramClass cClass =
appInfo.definitionFor(buildType(C.class, appInfo.dexItemFactory())).asProgramClass();
DexMethod bar = buildMethod(B.class.getMethod("foo"), appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(bar);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(bar);
assertEquals(
OptionalBool.TRUE, resolutionResult.isAccessibleForVirtualDispatchFrom(cClass, appInfo));
}
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java
index 6c6d9fe..4911f70 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/AbstractAllTest.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.AndroidApp;
import com.google.common.collect.ImmutableList;
@@ -45,7 +45,7 @@
buildClasses(CLASSES).addLibraryFile(parameters.getDefaultRuntimeLibrary()).build();
AppInfoWithLiveness appInfo = computeAppViewWithLiveness(app, Main.class).appInfo();
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
// Currently R8 will resolve to L::f as that is the first in the topological search.
// Resolution may return any of the matches, so it is valid if this expectation changes.
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java
index 273fb50..d6edf4f 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
@@ -50,7 +50,7 @@
Main.class)
.appInfo();
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
assertEquals(L.class.getTypeName(), resolutionTarget.getHolderType().toSourceString());
}
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java
index 9897acd..77fad9a 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
@@ -50,7 +50,7 @@
Main.class)
.appInfo();
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
assertEquals(R.class.getTypeName(), resolutionTarget.getHolderType().toSourceString());
}
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java
index baa7747..49edcae 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractLeftTest.java
@@ -13,7 +13,7 @@
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
@@ -53,7 +53,7 @@
Main.class)
.appInfo();
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
assertEquals(L.class.getTypeName(), resolutionTarget.getHolderType().toSourceString());
}
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java
index 904640e..1aa65c6 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAbstractRightTest.java
@@ -13,7 +13,7 @@
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
@@ -53,7 +53,7 @@
Main.class)
.appInfo();
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
assertEquals(R.class.getTypeName(), resolutionTarget.getHolderType().toSourceString());
}
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java
index 6406ea0..41307e8 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -52,7 +52,7 @@
Main.class)
.appInfo();
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
Set<String> holders = new HashSet<>();
resolutionResult
.asFailedResolution()
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java
index de6da74..9b9a45d 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableList;
import java.util.List;
@@ -46,7 +46,7 @@
Main.class)
.appInfo();
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
assertEquals(L.class.getTypeName(), resolutionTarget.getHolderType().toSourceString());
}
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java
index 7325cc1..50dc8d8 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableList;
import java.util.List;
@@ -46,7 +46,7 @@
Main.class)
.appInfo();
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
assertEquals(R.class.getTypeName(), resolutionTarget.getHolderType().toSourceString());
}
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java
index 296f95f..0139049 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -53,7 +53,7 @@
Main.class)
.appInfo();
DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
Set<String> holders = new HashSet<>();
resolutionResult
.asFailedResolution()
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java
index fbece8f..f8a46ca 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
@@ -60,7 +60,8 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(I.class, "bar", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method.holder, method);
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOnInterface(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java
index 8d0eb98..4449400 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
@@ -59,7 +59,8 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(I.class, "bar", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method.holder, method);
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOnInterface(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java
index e7532b1..6701adc 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
@@ -60,7 +60,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
@@ -107,7 +107,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java
index 63d9316..ae340f7 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
@@ -60,7 +60,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java
index 5f35faa..9af861c 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
@@ -59,7 +59,8 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(J.class, "bar", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method.holder, method);
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOnInterface(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleImplementsTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleImplementsTest.java
index 112c684..5912e69 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleImplementsTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleImplementsTest.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
@@ -60,7 +60,8 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method.holder, method);
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOnInterface(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SimpleInterfaceInvokeTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SimpleInterfaceInvokeTest.java
index d011624..3503bdf 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SimpleInterfaceInvokeTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SimpleInterfaceInvokeTest.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
@@ -60,7 +60,8 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method.holder, method);
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOnInterface(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubInterfaceOverridesTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubInterfaceOverridesTest.java
index 9caa8d2..512e775 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubInterfaceOverridesTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubInterfaceOverridesTest.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
@@ -60,7 +60,8 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method.holder, method);
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOnInterface(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeMissingOverridesTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeMissingOverridesTest.java
index aef0b3d..682d447 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeMissingOverridesTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeMissingOverridesTest.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
@@ -59,7 +59,8 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method.holder, method);
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOnInterface(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeOverridesTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeOverridesTest.java
index d6230c7..bbf73d7 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeOverridesTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubTypeOverridesTest.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
@@ -59,7 +59,8 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method.holder, method);
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOnInterface(method.holder, method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java
index 129c033..fe3a74c 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java
@@ -17,7 +17,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.resolution.packageprivate.a.Abstract;
import com.android.tools.r8.resolution.packageprivate.a.I;
import com.android.tools.r8.resolution.packageprivate.a.NonAbstract;
@@ -72,7 +72,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(Abstract.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Abstract.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryTest.java
index 9d06f40..4b2877d 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryTest.java
@@ -18,7 +18,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.resolution.packageprivate.a.A;
import com.android.tools.r8.resolution.packageprivate.a.A.B;
import com.android.tools.r8.resolution.packageprivate.a.D;
@@ -59,7 +59,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(A.class, "bar", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexProgramClass context =
appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryWithNarrowingTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryWithNarrowingTest.java
index a9eddd1..be967fb 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryWithNarrowingTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateReentryWithNarrowingTest.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.resolution.packageprivate.PackagePrivateReentryTest.C;
import com.android.tools.r8.resolution.packageprivate.a.A;
import com.android.tools.r8.resolution.packageprivate.a.A.B;
@@ -63,7 +63,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(A.class, "bar", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexProgramClass context =
appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethod2Test.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethod2Test.java
index fd40588..8262061 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethod2Test.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethod2Test.java
@@ -21,7 +21,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.resolution.packageprivate.a.Abstract;
import com.android.tools.r8.resolution.packageprivate.a.AbstractWidening;
import com.android.tools.r8.resolution.packageprivate.a.I;
@@ -73,7 +73,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexProgramClass context =
appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java
index 58f4f6d..876a991 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateWithDefaultMethodTest.java
@@ -19,7 +19,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.resolution.packageprivate.a.Abstract;
import com.android.tools.r8.resolution.packageprivate.a.I;
import com.android.tools.r8.resolution.packageprivate.a.J;
@@ -67,7 +67,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexProgramClass context =
appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
assertTrue(resolutionResult.isAccessibleFrom(context, appView).isFalse());
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/WidenAccessOutsidePackageTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/WidenAccessOutsidePackageTest.java
index d5b9556..219c239 100644
--- a/src/test/java/com/android/tools/r8/resolution/packageprivate/WidenAccessOutsidePackageTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/WidenAccessOutsidePackageTest.java
@@ -19,7 +19,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.resolution.packageprivate.a.A;
import com.android.tools.r8.resolution.packageprivate.a.A.B;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -60,7 +60,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(A.class, "bar", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexProgramClass context =
appView.definitionForProgramType(buildType(A.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java b/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java
index e116329..a8bbdf4 100644
--- a/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java
@@ -19,8 +19,8 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.LookupResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -118,7 +118,7 @@
DexMethod fooA = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
DexMethod fooB = buildNullaryVoidMethod(B.class, "foo", appInfo.dexItemFactory());
DexMethod fooC = buildNullaryVoidMethod(C.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolution = appInfo.resolveMethodOnClass(fooA);
+ MethodResolutionResult resolution = appInfo.resolveMethodOnClass(fooA);
DexProgramClass context = appView.definitionForProgramType(typeMain);
DexProgramClass upperBound = appView.definitionForProgramType(typeA);
DexProgramClass lowerBound = appView.definitionForProgramType(typeC);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java
index ee3387e..4da75f6 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/AbstractInMiddleTest.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
@@ -59,7 +59,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java
index 73b0af5..f6368cb 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
@@ -59,7 +59,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java
index 1f27130..4673e7c 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
@@ -59,7 +59,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java
index 11e259c..d6d5dc8 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
@@ -60,7 +60,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvalidResolutionToThisTarget.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvalidResolutionToThisTarget.java
index f19413c..10c5b6f 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvalidResolutionToThisTarget.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvalidResolutionToThisTarget.java
@@ -19,7 +19,7 @@
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.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.DescriptorUtils;
import java.io.IOException;
@@ -59,7 +59,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
assertTrue(resolutionResult.isSingleResolution());
DexType mainType = buildType(Main.class, appInfo.dexItemFactory());
DexProgramClass main = appView.definitionForProgramType(mainType);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java
index 511d697..e6e6f6c 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
@@ -59,7 +59,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
index d630f6f..3b675c7 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
@@ -19,7 +19,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.LookupResult;
import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.google.common.collect.ImmutableSet;
@@ -87,7 +87,7 @@
});
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(initial, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexProgramClass context =
DexProgramClass.asProgramClassOrNull(
appView
@@ -238,7 +238,7 @@
.build());
AppInfoWithClassHierarchy appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(B.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexType typeA = buildType(A.class, appInfo.dexItemFactory());
DexType typeB = buildType(B.class, appInfo.dexItemFactory());
DexProgramClass classB = appInfo.definitionForProgramType(typeB);
@@ -273,7 +273,7 @@
Unrelated.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(Unrelated.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Unrelated.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java
index 3fc0355..001075d 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateChainTest.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.resolution.virtualtargets.package_a.Middle;
import com.android.tools.r8.resolution.virtualtargets.package_a.Top;
import com.android.tools.r8.resolution.virtualtargets.package_a.TopRunner;
@@ -62,7 +62,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(Top.class, "clear", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexProgramClass context =
appView.definitionForProgramType(buildType(TopRunner.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java
index f081ef1..84021e7 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PackagePrivateFinalOverrideTest.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.resolution.virtualtargets.package_a.ViewModel;
import com.android.tools.r8.resolution.virtualtargets.package_a.ViewModelRunner;
import com.android.tools.r8.resolution.virtualtargets.package_a.ViewModelRunnerWithCast;
@@ -68,7 +68,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(ViewModel.class, "clear", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexProgramClass context =
appView.definitionForProgramType(
buildType(ViewModelRunner.class, appInfo.dexItemFactory()));
@@ -119,7 +119,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(ViewModel.class, "clear", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
@@ -171,7 +171,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(ViewModel.class, "clear", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexProgramClass context =
appView.definitionForProgramType(
buildType(ViewModelRunnerWithCast.class, appInfo.dexItemFactory()));
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PrivateOverrideOfVirtualTargetTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PrivateOverrideOfVirtualTargetTest.java
index 86e9ecb..eda5153 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/PrivateOverrideOfVirtualTargetTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/PrivateOverrideOfVirtualTargetTest.java
@@ -17,7 +17,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.resolution.virtualtargets.package_a.A;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Box;
@@ -61,7 +61,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(A.class, "bar", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexProgramClass context =
appView.definitionForProgramType(buildType(B.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedDifferentPackageLookupTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedDifferentPackageLookupTest.java
index 092f9b5..3a4a369 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedDifferentPackageLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedDifferentPackageLookupTest.java
@@ -16,7 +16,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -54,7 +54,7 @@
AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(builder.build(), Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedSamePackageLookupTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedSamePackageLookupTest.java
index 8915fa5..10fb0d9 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedSamePackageLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/ProtectedSamePackageLookupTest.java
@@ -15,7 +15,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -50,7 +50,7 @@
PackagePrivateChainTest.Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
index 092bcb8..536f420 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
@@ -21,7 +21,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.ResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
@@ -60,7 +60,7 @@
Main.class);
AppInfoWithLiveness appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory());
- ResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method);
DexProgramClass context =
appView.definitionForProgramType(buildType(Main.class, appInfo.dexItemFactory()));
LookupResult lookupResult = resolutionResult.lookupVirtualDispatchTargets(context, appInfo);
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
index 24fad30..e0eb6ca 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
@@ -74,7 +74,6 @@
}
private int getObfuscatedLinePosition() {
- // TODO(b/185358363): This should go away when we correctly retrace.
return kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72) ? 43 : 32;
}
@@ -122,10 +121,7 @@
8,
FILENAME_INLINE),
LinePosition.create(
- mainSubject.asFoundMethodSubject(),
- 1,
- getObfuscatedLinePosition(),
- FILENAME_INLINE));
+ mainSubject.asFoundMethodSubject(), 1, 21, FILENAME_INLINE));
checkInlineInformation(stackTrace, codeInspector, mainSubject, inlineStack);
});
}
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
index ed842eb..032a370 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
@@ -122,7 +122,7 @@
LinePosition.stack(
LinePosition.create(
inlineExceptionStatic(kotlinInspector), 2, 8, FILENAME_INLINE_STATIC),
- LinePosition.create(mainSubject.asFoundMethodSubject(), 2, 15, mainFileName));
+ LinePosition.create(mainSubject.asFoundMethodSubject(), 2, 9, mainFileName));
checkInlineInformation(stackTrace, codeInspector, mainSubject, inlineStack);
});
}
@@ -155,7 +155,7 @@
2,
15,
FILENAME_INLINE_INSTANCE),
- LinePosition.create(mainSubject.asFoundMethodSubject(), 2, 13, mainFileName));
+ LinePosition.create(mainSubject.asFoundMethodSubject(), 2, 7, mainFileName));
checkInlineInformation(stackTrace, codeInspector, mainSubject, inlineStack);
});
}
@@ -187,7 +187,7 @@
inlineExceptionStatic(kotlinInspector), 3, 8, FILENAME_INLINE_STATIC),
// TODO(b/146399675): There should be a nested frame on
// retrace.NestedInlineFunctionKt.nestedInline(line 10).
- LinePosition.create(mainSubject.asFoundMethodSubject(), 3, 19, mainFileName));
+ LinePosition.create(mainSubject.asFoundMethodSubject(), 3, 10, mainFileName));
checkInlineInformation(stackTrace, codeInspector, mainSubject, inlineStack);
});
}
@@ -218,8 +218,8 @@
LinePosition.create(
inlineExceptionStatic(kotlinInspector), 2, 8, FILENAME_INLINE_STATIC),
// TODO(b/146399675): There should be a nested frame on
- // retrace.NestedInlineFunctionKt.nestedInlineOnFirstLine(line 15).
- LinePosition.create(mainSubject.asFoundMethodSubject(), 2, 20, mainFileName));
+ // retrace.NestedInlineFunctionKt.nestedInline(line 10).
+ LinePosition.create(mainSubject.asFoundMethodSubject(), 2, 10, mainFileName));
checkInlineInformation(stackTrace, codeInspector, mainSubject, inlineStack);
});
}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationJacocoTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationJacocoTest.java
index 11829f6..f860ca2 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationJacocoTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationJacocoTest.java
@@ -100,7 +100,7 @@
List<String> cmdline = new ArrayList<>();
cmdline.add(TestRuntime.getSystemRuntime().asCf().getJavaExecutable().toString());
cmdline.add("-jar");
- cmdline.add(ToolHelper.JACOCO_CLI);
+ cmdline.add(ToolHelper.JACOCO_CLI.toString());
cmdline.add("instrument");
cmdline.add(input.toString());
cmdline.add("--dest");
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/NoLongerSatisfiedIfRuleTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/NoLongerSatisfiedIfRuleTest.java
index 02dde28..fdef324 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/NoLongerSatisfiedIfRuleTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/NoLongerSatisfiedIfRuleTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.shaking.ifrule;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -51,8 +52,7 @@
ClassSubject bClassSubject = inspector.clazz(B.class);
assertThat(bClassSubject, isPresent());
- // TODO(b/153910208): Should be absent since A is dead.
- assertThat(bClassSubject.uniqueMethodWithName("m"), isPresent());
+ assertThat(bClassSubject.uniqueMethodWithName("m"), isAbsent());
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/KeepIfPresentRuleWithVerticalClassMergingTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/KeepIfPresentRuleWithVerticalClassMergingTest.java
index 541253a..8fab4c1 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/KeepIfPresentRuleWithVerticalClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/KeepIfPresentRuleWithVerticalClassMergingTest.java
@@ -26,7 +26,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public KeepIfPresentRuleWithVerticalClassMergingTest(TestParameters parameters) {
@@ -41,7 +41,7 @@
.addKeepRules(
"-if class * extends " + A.class.getTypeName(), "-keep class <1> { <init>(...); }")
.enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
.inspect(
inspector -> {
@@ -53,8 +53,7 @@
assertThat(classBSubject.init(), isPresent());
assertThat(classBSubject.uniqueMethodWithName("greet"), isPresent());
assertEquals(2, classBSubject.allMethods().size());
- }
- )
+ })
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutputLines("Hello world!");
}
diff --git a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
index accb38b..e2b6d2e 100644
--- a/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/includedescriptorclasses/IncludeDescriptorClassesTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -27,9 +28,9 @@
this.testParameters = parameters;
}
- @Parameterized.Parameters(name = "{0}, horizontalClassMerging:{1}")
- public static List<Object[]> data() {
- return buildParameters(getTestParameters().withAllRuntimesAndApiLevels().build());
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private class Result {
diff --git a/src/test/java/com/android/tools/r8/shaking/keepclassmembers/UnsatisfiedDependentNoObfuscationTest.java b/src/test/java/com/android/tools/r8/shaking/keepclassmembers/UnsatisfiedDependentNoObfuscationTest.java
index a146368..efdfb71 100644
--- a/src/test/java/com/android/tools/r8/shaking/keepclassmembers/UnsatisfiedDependentNoObfuscationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/keepclassmembers/UnsatisfiedDependentNoObfuscationTest.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.shaking.keepclassmembers;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverInline;
@@ -34,8 +34,8 @@
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
.addKeepRules(
- // TODO(b/192636793): This rule should not have any impact on the compilation, since
- // GreeterConsumer is dead.
+ // This rule should not have any impact on the compilation, since GreeterConsumer is
+ // dead.
"-keepclassmembers,includedescriptorclasses class "
+ GreeterConsumer.class.getTypeName()
+ " {",
@@ -44,11 +44,7 @@
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
- .inspect(
- inspector -> {
- // TODO(b/192636793): This should be renamed.
- assertThat(inspector.clazz(Greeter.class), isPresentAndNotRenamed());
- })
+ .inspect(inspector -> assertThat(inspector.clazz(Greeter.class), isPresentAndRenamed()))
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("Hello");
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
index 402d6b3..4ea9a82 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
@@ -120,6 +120,11 @@
}
@Override
+ public boolean isInterface() {
+ throw new Unreachable("Cannot determine if an absent class is an interface");
+ }
+
+ @Override
public String getOriginalName() {
return reference.getTypeName();
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index 8beec8a..f552a25 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -171,6 +171,8 @@
@Override
public abstract ClassAccessFlags getAccessFlags();
+ public abstract boolean isInterface();
+
public abstract boolean isAbstract();
public abstract boolean isAnnotation();
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index 1114bce..318f0d9 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -294,6 +294,11 @@
}
@Override
+ public boolean isInterface() {
+ return dexClass.isInterface();
+ }
+
+ @Override
public boolean isImplementing(ClassSubject subject) {
assertTrue(subject.isPresent());
for (DexType itf : getDexProgramClass().interfaces) {
diff --git a/src/test/java/com/android/tools/r8/utils/graphinspector/GraphInspector.java b/src/test/java/com/android/tools/r8/utils/graphinspector/GraphInspector.java
index 45ffbf2..11fcaa4 100644
--- a/src/test/java/com/android/tools/r8/utils/graphinspector/GraphInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/graphinspector/GraphInspector.java
@@ -584,6 +584,7 @@
&& EdgeKindPredicate.isAnnotatedOn.test(infos));
}
+ @SuppressWarnings("unchecked")
@Override
public boolean isKeptByReferenceInAnnotationOn(
QueryNode annotationNode, QueryNode annotatedNode) {
@@ -913,7 +914,7 @@
}
private QueryNode getQueryNode(GraphNode node, String absentString) {
- return node == null ? new AbsentQueryNode(absentString) : new QueryNodeImpl(this, node);
+ return node == null ? new AbsentQueryNode(absentString) : new QueryNodeImpl<>(this, node);
}
private boolean isPureCompatTarget(GraphNode target) {
diff --git a/third_party/android_jar/api-database.tar.gz.sha1 b/third_party/android_jar/api-database.tar.gz.sha1
index 97d5f12..2bbe555 100644
--- a/third_party/android_jar/api-database.tar.gz.sha1
+++ b/third_party/android_jar/api-database.tar.gz.sha1
@@ -1 +1 @@
-a3e0351d71082eb74073576e11c0632191fd8530
\ No newline at end of file
+829d7f32a482c16a2008a8878c33c58637c21a56
\ No newline at end of file
diff --git a/third_party/jacoco/0.8.6.tar.gz.sha1 b/third_party/jacoco/0.8.6.tar.gz.sha1
index 47fefc1..8ff8131 100644
--- a/third_party/jacoco/0.8.6.tar.gz.sha1
+++ b/third_party/jacoco/0.8.6.tar.gz.sha1
@@ -1 +1 @@
-471ccc4aa6d69a684aa400f08dc6a15e17b4ba25
\ No newline at end of file
+b3927f234695ffd3004d248741a125782a6b93b7
\ No newline at end of file
diff --git a/third_party/openjdk/jdk-16/linux.tar.gz.sha1 b/third_party/openjdk/jdk-16/linux.tar.gz.sha1
new file mode 100644
index 0000000..0a22059
--- /dev/null
+++ b/third_party/openjdk/jdk-16/linux.tar.gz.sha1
@@ -0,0 +1 @@
+8fa0d8caeed71708c442eabb610b794c1efa63a3
\ No newline at end of file
diff --git a/third_party/openjdk/jdk-16/osx.tar.gz.sha1 b/third_party/openjdk/jdk-16/osx.tar.gz.sha1
new file mode 100644
index 0000000..ebbc803
--- /dev/null
+++ b/third_party/openjdk/jdk-16/osx.tar.gz.sha1
@@ -0,0 +1 @@
+b7db69925ff470d6b118e70f28f39451afb027b7
\ No newline at end of file
diff --git a/third_party/openjdk/jdk-16/windows.tar.gz.sha1 b/third_party/openjdk/jdk-16/windows.tar.gz.sha1
new file mode 100644
index 0000000..fe6a7bd
--- /dev/null
+++ b/third_party/openjdk/jdk-16/windows.tar.gz.sha1
@@ -0,0 +1 @@
+ce1494575976dbc8ac4702ef6f08c15cc6b38c47
\ No newline at end of file
diff --git a/tools/retrace.py b/tools/retrace.py
index c087b31..d28c222 100755
--- a/tools/retrace.py
+++ b/tools/retrace.py
@@ -46,10 +46,15 @@
action='store_true',
help='Disables diagnostics printing to stdout.')
parser.add_argument(
- '--debug-agent',
- default=None,
- action='store_true',
- help='Attach a debug-agent to the retracer java process.')
+ '--debug-agent',
+ default=None,
+ action='store_true',
+ help='Attach a debug-agent to the retracer java process.')
+ parser.add_argument(
+ '--regex',
+ default=None,
+ help='Sets a custom regular expression used for parsing'
+ )
return parser.parse_args()
@@ -62,9 +67,10 @@
args.stacktrace,
args.no_r8lib,
quiet=args.quiet,
- debug=args.debug_agent)
+ debug=args.debug_agent,
+ regex=args.regex)
-def run(map_path, stacktrace, no_r8lib, quiet=False, debug=False):
+def run(map_path, stacktrace, no_r8lib, quiet=False, debug=False, regex=None):
retrace_args = [jdk.GetJavaExecutable()]
if debug:
@@ -78,6 +84,10 @@
map_path
]
+ if regex:
+ retrace_args.append('--regex')
+ retrace_args.append(regex)
+
if quiet:
retrace_args.append('--quiet')
diff --git a/tools/test.py b/tools/test.py
index 157bd28..9c35352 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -171,6 +171,10 @@
help='Print the execution time of the slowest tests..',
default=False, action='store_true')
result.add_option(
+ '--testing-state-name',
+ help='Set an explict name for the testing state '
+ '(used in conjunction with --with/reset-testing-state).')
+ result.add_option(
'--with-testing-state',
help='Run/resume tests using testing state.',
default=False, action='store_true')
@@ -322,6 +326,8 @@
gradle_args.append('-Preset-testing-state')
elif options.with_testing_state:
gradle_args.append('-Ptesting-state')
+ if options.testing_state_name:
+ gradle_args.append('-Ptesting-state-name=' + options.testing_state_name)
# Build an R8 with dependencies for bootstrapping tests before adding test sources.
gradle_args.append('r8WithDeps')