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..f860ca25 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')