Merge commit 'b84da85ab9d8569f7f8ee41238c527434a9f2c14' into dev-release
diff --git a/build.gradle b/build.gradle
index 12d14c9..92e940a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -284,7 +284,6 @@
def r8LibPath = "$buildDir/libs/r8lib.jar"
def r8LibExludeDepsPath = "$buildDir/libs/r8lib-exclude-deps.jar"
def r8LibGeneratedKeepRulesPath = "$buildDir/generated/keep.txt"
-def r8LibGeneratedKeepRulesExcludeDepsPath = "$buildDir/generated/keep-exclude-deps.txt"
def r8LibTestPath = "$buildDir/classes/r8libtest"
def java11ClassFiles = "build/classes/java/mainJava11"
@@ -402,6 +401,7 @@
"third_party": [
"benchmarks/kotlin-benches",
"chrome/chrome_180917_ffbaa8",
+ "chrome/chrome_200430",
"classlib",
"cf_segments",
"desugar/desugar_20180308",
@@ -695,33 +695,9 @@
}
}
-static configureRelocations(ShadowJar task) {
- task.relocate('com.google.common', 'com.android.tools.r8.com.google.common')
- task.relocate('com.google.gson', 'com.android.tools.r8.com.google.gson')
- task.relocate('com.google.thirdparty', 'com.android.tools.r8.com.google.thirdparty')
- task.relocate('joptsimple', 'com.android.tools.r8.joptsimple')
- task.relocate('org.objectweb.asm', 'com.android.tools.r8.org.objectweb.asm')
- task.relocate('it.unimi.dsi.fastutil', 'com.android.tools.r8.it.unimi.dsi.fastutil')
- task.relocate('kotlin', 'com.android.tools.r8.jetbrains.kotlin')
- task.relocate('kotlinx', 'com.android.tools.r8.jetbrains.kotlinx')
- task.relocate('org.jetbrains', 'com.android.tools.r8.org.jetbrains')
- task.relocate('org.intellij', 'com.android.tools.r8.org.intellij')
-}
-
-task repackageDepsNoRelocate(type: ShadowJar) {
- configurations = [project.configurations.runtimeClasspath]
- mergeServiceFiles(it)
- exclude { it.getRelativePath().getPathString() == "module-info.class" }
- exclude { it.getRelativePath().getPathString().startsWith("META-INF/maven/") }
- baseName 'deps-not-relocated'
-}
-
task repackageDeps(type: ShadowJar) {
configurations = [project.configurations.runtimeClasspath]
mergeServiceFiles(it)
- if (!project.hasProperty('lib_no_relocate')) {
- configureRelocations(it)
- }
exclude { it.getRelativePath().getPathString() == "module-info.class" }
exclude { it.getRelativePath().getPathString().startsWith("META-INF/maven/") }
baseName 'deps'
@@ -730,9 +706,6 @@
task repackageSources(type: ShadowJar) {
from sourceSets.main.output
mergeServiceFiles(it)
- if (!project.hasProperty('lib_no_relocate')) {
- configureRelocations(it)
- }
baseName 'sources'
}
@@ -740,100 +713,161 @@
dependsOn compileMainWithJava11
from file(java11ClassFiles)
mergeServiceFiles(it)
- if (!project.hasProperty('lib_no_relocate')) {
- configureRelocations(it)
- }
baseName 'sources11'
}
-task r8WithRelocatedDeps(type: ShadowJar) {
- from consolidatedLicense.outputs.files
- baseName 'r8_with_relocated_deps'
- classifier = null
- version = null
- manifest {
- attributes 'Main-Class': 'com.android.tools.r8.SwissArmyKnife'
+def r8CreateTask(name, baseNameName, sources, includeSwissArmyKnife) {
+ return tasks.create("r8Create${name}", ShadowJar) {
+ from consolidatedLicense.outputs.files
+ from sources
+ baseName baseNameName
+ classifier = null
+ version = null
+ if (includeSwissArmyKnife) {
+ manifest {
+ attributes 'Main-Class': 'com.android.tools.r8.SwissArmyKnife'
+ }
+ }
+ exclude "META-INF/*.kotlin_module"
+ exclude "**/*.kotlin_metadata"
}
- from repackageSources.outputs.files
- from repackageDeps.outputs.files
- configureRelocations(it)
- exclude "META-INF/*.kotlin_module"
- exclude "**/*.kotlin_metadata"
}
-task r8WithRelocatedDeps11(type: ShadowJar) {
- from consolidatedLicense.outputs.files
- baseName 'r8_with_relocated_deps_11'
- classifier = null
- version = null
- manifest {
- attributes 'Main-Class': 'com.android.tools.r8.SwissArmyKnife'
+def r8RelocateTask(r8Task, output) {
+ return tasks.create("r8Relocate_${r8Task.name}", Exec) {
+ dependsOn r8WithDeps
+ dependsOn r8Task
+ outputs.file output
+ workingDir = projectDir
+ inputs.files r8Task.outputs.files + r8WithDeps.outputs.files
+ commandLine baseR8CommandLine([
+ "relocator",
+ "--input",
+ r8Task.outputs.files[0],
+ "--output",
+ output,
+ "--map",
+ "com.google.common->com.android.tools.r8.com.google.common",
+ "--map",
+ "com.google.gson->com.android.tools.r8.com.google.gson",
+ "--map",
+ "com.google.thirdparty->com.android.tools.r8.com.google.thirdparty",
+ "--map",
+ "joptsimple->com.android.tools.r8.joptsimple",
+ "--map",
+ "org.objectweb.asm->com.android.tools.r8.org.objectweb.asm",
+ "--map",
+ "it.unimi.dsi.fastutil->com.android.tools.r8.it.unimi.dsi.fastutil",
+ "--map",
+ "kotlin->com.android.tools.r8.jetbrains.kotlin",
+ "--map",
+ "kotlinx->com.android.tools.r8.jetbrains.kotlinx",
+ "--map",
+ "org.jetbrains->com.android.tools.r8.org.jetbrains",
+ "--map",
+ "org.intellij->com.android.tools.r8.org.intellij"
+ ])
}
- from repackageSources11.outputs.files
- from repackageDeps.outputs.files
- configureRelocations(it)
- exclude "META-INF/*.kotlin_module"
- exclude "**/*.kotlin_metadata"
}
-task r8WithoutDeps(type: ShadowJar) {
- from consolidatedLicense.outputs.files
- baseName 'r8_without_deps'
- classifier = null
- version = null
- manifest {
- attributes 'Main-Class': 'com.android.tools.r8.SwissArmyKnife'
- }
- from sourceSets.main.output
+task r8WithDeps {
+ dependsOn repackageSources
+ dependsOn repackageDeps
+ def r8Task = r8CreateTask(
+ 'WithDeps',
+ 'r8_with_deps',
+ repackageSources.outputs.files + repackageDeps.outputs.files,
+ true)
+ dependsOn r8Task
+ outputs.files r8Task.outputs.files
}
-task R8(type: ShadowJar) {
- from consolidatedLicense.outputs.files
- baseName 'r8'
- classifier = null
- version = null
- manifest {
- attributes 'Main-Class': 'com.android.tools.r8.SwissArmyKnife'
- }
- // In order to build without dependencies, pass the exclude_deps property using:
- // gradle -Pexclude_deps R8
- if (!project.hasProperty('exclude_deps')) {
- from repackageSources.outputs.files
- from repackageDeps.outputs.files
- } else {
- from sourceSets.main.output
- }
- exclude "META-INF/*.kotlin_module"
- exclude "**/*.kotlin_metadata"
+task r8WithDeps11 {
+ dependsOn repackageSources11
+ dependsOn repackageDeps
+ def r8Task = r8CreateTask(
+ 'WithDeps11',
+ 'r8_with_deps_11',
+ repackageSources11.outputs.files + repackageDeps.outputs.files,
+ true)
+ dependsOn r8Task
+ outputs.files r8Task.outputs.files
}
-task R8NoManifestNoDeps(type: ShadowJar) {
- from consolidatedLicense.outputs.files
- baseName 'r8nomanifest-exclude-deps'
- classifier = null
- version = null
- from sourceSets.main.output
+task r8WithRelocatedDeps {
+ def output = "${buildDir}/libs/r8_with_relocated_deps.jar"
+ dependsOn r8RelocateTask(r8WithDeps, output)
+ outputs.file output
}
-task R8NoManifest(type: ShadowJar) {
- from consolidatedLicense.outputs.files
- baseName 'r8nomanifest'
- classifier = null
- version = null
- // In order to build without dependencies, pass the exclude_deps property using:
- // gradle -Pexclude_deps R8
- if (!project.hasProperty('exclude_deps')) {
- from repackageSources.outputs.files
- from repackageDeps.outputs.files
- } else {
- from sourceSets.main.output
- }
- exclude "META-INF/*.kotlin_module"
- exclude "**/*.kotlin_metadata"
+task r8WithRelocatedDeps11 {
+ def output = "${buildDir}/libs/r8_with_relocated_deps_11.jar"
+ dependsOn r8RelocateTask(r8WithDeps11, output)
+ outputs.file output
+}
+
+task r8WithoutDeps {
+ dependsOn repackageSources
+ def r8Task = r8CreateTask(
+ 'WithoutDeps',
+ 'r8_without_deps',
+ repackageSources.outputs.files,
+ true)
+ dependsOn r8Task
+ outputs.files r8Task.outputs.files
+}
+
+task r8(type: Copy) {
+ def r8Task = project.hasProperty("exclude_deps")
+ ? r8WithoutDeps : r8WithRelocatedDeps
+ dependsOn r8Task
+ from r8Task.outputs.files[0]
+ into file("${buildDir}/libs")
+ rename { String fileName -> "r8.jar" }
+ outputs.file "${buildDir}/libs/r8.jar"
+}
+
+task r8NoManifestWithoutDeps {
+ dependsOn repackageSources
+ def r8Task = r8CreateTask(
+ 'NoManifestWithoutDeps',
+ 'r8_no_manifest_without_deps',
+ repackageSources.outputs.files,
+ false)
+ dependsOn r8Task
+ outputs.files r8Task.outputs.files
+}
+
+task r8NoManifestWithDeps {
+ dependsOn repackageSources
+ def r8Task = r8CreateTask(
+ 'NoManifestWithDeps',
+ 'r8_no_manifest_with_deps',
+ repackageSources.outputs.files + repackageDeps.outputs.files,
+ false)
+ dependsOn r8Task
+ outputs.files r8Task.outputs.files
+}
+
+task r8NoManifestWithRelocatedDeps {
+ def output = "${buildDir}/libs/r8_no_manifest_with_relocated_deps.jar"
+ dependsOn r8RelocateTask(r8NoManifestWithDeps, output)
+ outputs.file output
+}
+
+task r8NoManifest(type: Copy) {
+ def r8Task = project.hasProperty("exclude_deps")
+ ? r8NoManifestWithoutDeps : r8NoManifestWithRelocatedDeps
+ dependsOn r8Task
+ from r8Task.outputs.files[0]
+ into file("${buildDir}/libs")
+ rename { String fileName -> "r8_no_manifest.jar" }
+ outputs.file "${buildDir}/libs/r8_no_manifest.jar"
}
task D8(type: ShadowJar) {
- from R8.outputs.files
+ dependsOn r8
+ from r8.outputs.files[0]
baseName 'd8'
manifest {
attributes 'Main-Class': 'com.android.tools.r8.D8'
@@ -841,10 +875,10 @@
}
def baseR8CommandLine(args = []) {
- // Execute r8 commands against a stable r8 with relocated dependencies.
+ // Execute r8 commands against a stable r8 with dependencies.
// TODO(b/139725780): See if we can remove or lower the heap size (-Xmx6g).
return [org.gradle.internal.jvm.Jvm.current().getJavaExecutable(),
- "-Xmx8g", "-ea", "-jar", r8WithRelocatedDeps.outputs.files[0]] + args
+ "-Xmx8g", "-ea", "-jar", r8WithDeps.outputs.files[0]] + args
}
def r8CfCommandLine(input, output, pgConfs = [], args = ["--release"], libs = []) {
@@ -877,36 +911,57 @@
destinationDir file('build/libs')
}
-task testJar(type: ShadowJar, dependsOn: [testClasses, buildLibraryDesugarConversions]) {
- baseName = "r8tests"
+task testJarSources(type: ShadowJar, dependsOn: [testClasses, buildLibraryDesugarConversions]) {
+ baseName = "r8testsbase"
from sourceSets.test.output
// We only want to include tests that use R8 when generating keep rules for applymapping.
include "com/android/tools/r8/**"
include "dalvik/**"
}
+task testJar(type: Exec) {
+ dependsOn r8WithDeps
+ dependsOn testJarSources
+ def output = "$buildDir/libs/r8tests.jar"
+ outputs.file output
+ workingDir = projectDir
+ inputs.files ([testJarSources.outputs, r8WithDeps.outputs])
+ commandLine baseR8CommandLine([
+ "relocator",
+ "--input",
+ testJarSources.outputs.files[0],
+ "--output",
+ output,
+ "--map",
+ "kotlinx.metadata->com.android.tools.r8.jetbrains.kotlinx.metadata"
+ ])
+}
+
task generateR8LibKeepRules(type: Exec) {
doFirst {
// TODO(b/154785341): We should remove this.
standardOutput new FileOutputStream(r8LibGeneratedKeepRulesPath)
}
- dependsOn R8NoManifest
dependsOn r8WithRelocatedDeps
+ dependsOn r8NoManifestWithDeps
dependsOn testJar
dependsOn downloadOpenJDKrt
- inputs.files ([r8WithRelocatedDeps.outputs, R8NoManifest.outputs, testJar.outputs])
+ inputs.files ([
+ r8WithRelocatedDeps.outputs,
+ r8NoManifestWithDeps.outputs,
+ testJar.outputs])
outputs.file r8LibGeneratedKeepRulesPath
commandLine baseR8CommandLine([
"printuses",
"--keeprules-allowobfuscation",
"third_party/openjdk/openjdk-rt-1.8/rt.jar",
- R8NoManifest.outputs.files[0],
+ r8NoManifestWithDeps.outputs.files[0],
testJar.outputs.files[0]])
workingDir = projectDir
}
task R8LibApiOnly {
- dependsOn r8LibCreateTask("Api", ["src/main/keep.txt"], R8NoManifest, r8LibPath)
+ dependsOn r8LibCreateTask("Api", ["src/main/keep.txt"], r8NoManifest, r8LibPath)
outputs.file r8LibPath
}
@@ -916,7 +971,7 @@
["src/main/keep.txt",
"src/main/keep-applymapping.txt",
generateR8LibKeepRules.outputs.files[0]],
- R8NoManifest,
+ r8NoManifestWithRelocatedDeps,
r8LibPath,
).dependsOn(generateR8LibKeepRules)
outputs.file r8LibPath
@@ -926,11 +981,11 @@
dependsOn r8LibCreateTask(
"NoDeps",
["src/main/keep.txt", "src/main/keep-applymapping.txt"],
- R8NoManifestNoDeps,
+ r8NoManifestWithoutDeps,
r8LibExludeDepsPath,
"--release",
- repackageDepsNoRelocate.outputs.files
- ).dependsOn(repackageDepsNoRelocate)
+ repackageDeps.outputs.files
+ ).dependsOn(repackageDeps)
outputs.file r8LibExludeDepsPath
}
@@ -1739,7 +1794,7 @@
task buildR8LibCfTestDeps(type: Exec) {
def outputPath = "build/libs/r8libtestdeps-cf.jar"
dependsOn downloadDeps
- dependsOn R8NoManifest
+ dependsOn r8NoManifest
dependsOn R8Lib
dependsOn generateR8TestKeepRules
dependsOn testJar
@@ -1754,8 +1809,8 @@
testJar.outputs.files[0],
outputPath,
[generateR8TestKeepRules.outputs.files[0]],
- ["--debug", "--classpath", R8NoManifest.outputs.files[0]],
- R8NoManifest.outputs.files + addedLibraries)
+ ["--debug", "--classpath", r8NoManifest.outputs.files[0]],
+ r8NoManifest.outputs.files + addedLibraries)
workingDir = projectDir
outputs.file outputPath
}
@@ -1823,7 +1878,7 @@
dependsOn buildLibraryDesugarConversions
dependsOn getJarsFromSupportLibs
// R8.jar is required for running bootstrap tests.
- dependsOn R8
+ dependsOn r8
testLogging.exceptionFormat = 'full'
if (project.hasProperty('print_test_stdout')) {
testLogging.showStandardStreams = true
diff --git a/src/library_desugar/desugar_jdk_libs.json b/src/library_desugar/desugar_jdk_libs.json
index aadb671..07765bb 100644
--- a/src/library_desugar/desugar_jdk_libs.json
+++ b/src/library_desugar/desugar_jdk_libs.json
@@ -1,6 +1,8 @@
{
- "configuration_format_version": 3,
- "version": "0.11.1",
+ "configuration_format_version": 4,
+ "group_id" : "com.tools.android",
+ "artifact_id" : "desugar_jdk_libs",
+ "version": "0.11.2",
"required_compilation_api_level": 26,
"synthesized_library_classes_package_prefix": "j$.",
"library_flags": [
diff --git a/src/library_desugar/desugar_jdk_libs_comments.md b/src/library_desugar/desugar_jdk_libs_comments.md
index f08da27..2ba5908 100644
--- a/src/library_desugar/desugar_jdk_libs_comments.md
+++ b/src/library_desugar/desugar_jdk_libs_comments.md
@@ -1,4 +1,4 @@
-# Description of the core library configuration file
+# Description of the desugared library configuration file
## Version
@@ -7,27 +7,33 @@
Non-backward compatible changes to the desugared library increase the version number, and such
library cannot be compiled without upgrading R8/D8 to the latest version.
-The second field `version` holds the version of the content for the configuration. This number
+The fields `group_id` and `artifact_id` are maven-coordinated ids for the desugared library
+configuration file.
+
+The field `version` holds the version of the content for the configuration. This number
must be updated each time the configuration is changed.
+A unique identifier is generated for the desugared library configuration using
+`group_id:artifact_id:version`.
+
## Required compilation API level
-The third field `required_compilation_api_level` encodes the minimal Android API level required for
+The field `required_compilation_api_level` encodes the minimal Android API level required for
the desugared library to be compiled correctly. If the API of library used for compilation of the
library or a program using the library is lower than this level, one has to upgrade the SDK version
used to be able to use desugared libraries.
## Library and program flags
-The fourth and fifth fields are `library_flags` and `program_flags`. They include the set of flags
-required for respectively the library and the program using the desugared library compilation. The
-sets of flags are different depending on the min API level used. The flags are in a list, where
-each list entry specifies up to which min API level the set of flags should be applied. During
-compilation, R8/D8 adds up all the required flags for the min API level specified at compilation.
+The fields `library_flags` and `program_flags` include the set of flags required for respectively
+the library and the program using the desugared library compilation. The sets of flags are
+different depending on the min API level used. The flags are in a list, where each list entry
+specifies up to which min API level the set of flags should be applied. During compilation,
+R8/D8 adds up all the required flags for the min API level specified at compilation.
-For example, let's say the `program_flags` have entries for `api_level_below_or_equal` 20, 24 and 26.
-If compiling the program for min API 24, R8/D8 will use both the set of flags for API 24 and 26
-(24 <= 24, 24 <= 26 but !(24 <= 20)).
+For example, let's say the `program_flags` have entries for `api_level_below_or_equal` 20, 24 and
+26. If compiling the program for min API 24, R8/D8 will use both the set of flags for API 24 and
+26 (24 <= 24, 24 <= 26 but !(24 <= 20)).
## Extra keep rules
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
index 410441b..586c4f1 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
@@ -36,17 +36,23 @@
" # is the default handling of javac assertion code when",
" # generating class file format.");
- void parsePositiveIntArgument(
- B builder, String flag, String argument, Origin origin, Consumer<Integer> setter) {
+ public static void parsePositiveIntArgument(
+ Consumer<Diagnostic> errorConsumer,
+ String flag,
+ String argument,
+ Origin origin,
+ Consumer<Integer> setter) {
int value;
try {
value = Integer.parseInt(argument);
} catch (NumberFormatException e) {
- builder.error(new StringDiagnostic("Invalid argument to " + flag + ": " + argument, origin));
+ errorConsumer.accept(
+ new StringDiagnostic("Invalid argument to " + flag + ": " + argument, origin));
return;
}
if (value < 1) {
- builder.error(new StringDiagnostic("Invalid argument to " + flag + ": " + argument, origin));
+ errorConsumer.accept(
+ new StringDiagnostic("Invalid argument to " + flag + ": " + argument, origin));
return;
}
setter.accept(value);
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 3c132e0..e8c6bb1 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -239,6 +239,7 @@
if (marker != null && hasClassResources) {
markers.add(marker);
}
+ Marker.checkCompatibleDesugaredLibrary(markers, options.reporter);
InspectorImpl.runInspections(options.outputInspections, app);
if (options.isGeneratingClassFiles()) {
@@ -250,7 +251,7 @@
GraphLense.getIdentityLense(),
NamingLens.getIdentityLens(),
null)
- .write(options.getClassFileConsumer(), executor);
+ .write(options.getClassFileConsumer());
} else {
NamingLens namingLens;
DexApplication finalApp = app;
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 2f6197f..164c572 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static com.android.tools.r8.utils.InternalOptions.DETERMINISTIC_DEBUGGING;
+
import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
import com.android.tools.r8.graph.DexItemFactory;
@@ -422,8 +424,10 @@
internal.outputInspections = InspectorImpl.wrapInspections(getOutputInspections());
- assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
- internal.threadCount = getThreadCount();
+ if (!DETERMINISTIC_DEBUGGING) {
+ assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
+ internal.threadCount = getThreadCount();
+ }
return internal;
}
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
index aad3468..3c7fdd2 100644
--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -252,12 +252,13 @@
builder.error(
new StringDiagnostic("Cannot set multiple " + MIN_API_FLAG + " options", origin));
} else {
- parsePositiveIntArgument(builder, MIN_API_FLAG, nextArg, origin, builder::setMinApiLevel);
+ parsePositiveIntArgument(
+ builder::error, MIN_API_FLAG, nextArg, origin, builder::setMinApiLevel);
hasDefinedApiLevel = true;
}
} else if (arg.equals(THREAD_COUNT_FLAG)) {
parsePositiveIntArgument(
- builder, THREAD_COUNT_FLAG, nextArg, origin, builder::setThreadCount);
+ builder::error, THREAD_COUNT_FLAG, nextArg, origin, builder::setThreadCount);
} else if (arg.equals("--intermediate")) {
builder.setIntermediate(true);
} else if (arg.equals("--no-desugaring")) {
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
index bf55656..7d5b31e 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -37,7 +37,6 @@
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.Sets;
import java.io.File;
@@ -326,7 +325,7 @@
ClassFileConsumer consumer =
new ClassFileConsumer.ArchiveConsumer(
lintFile(compilationApiLevel, minApiLevel, FileUtils.JAR_EXTENSION));
- writer.write(consumer, ThreadUtils.getExecutorService(options));
+ writer.write(consumer);
consumer.finished(options.reporter);
}
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index a647cf5..42290d2 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -7,12 +7,13 @@
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.EnqueuerFactory;
import com.android.tools.r8.shaking.MainDexClasses;
@@ -48,14 +49,16 @@
try {
DirectMappedDexApplication application =
new ApplicationReader(app, options, timing).read(executor).toDirect();
- AppView<? extends AppInfoWithSubtyping> appView =
- AppView.createForR8(new AppInfoWithSubtyping(application), options);
+ AppView<? extends AppInfoWithClassHierarchy> appView =
+ AppView.createForR8(new AppInfoWithClassHierarchy(application), options);
appView.setAppServices(AppServices.builder(appView).build());
MainDexListBuilder.checkForAssumedLibraryTypes(appView.appInfo());
+ SubtypingInfo subtypingInfo = new SubtypingInfo(application.allClasses(), application);
+
RootSet mainDexRootSet =
- new RootSetBuilder(appView, application, options.mainDexKeepRules).run(executor);
+ new RootSetBuilder(appView, subtypingInfo, options.mainDexKeepRules).run(executor);
GraphConsumer graphConsumer = options.mainDexKeptGraphConsumer;
WhyAreYouKeepingConsumer whyAreYouKeepingConsumer = null;
@@ -64,7 +67,8 @@
graphConsumer = whyAreYouKeepingConsumer;
}
- Enqueuer enqueuer = EnqueuerFactory.createForMainDexTracing(appView, graphConsumer);
+ Enqueuer enqueuer =
+ EnqueuerFactory.createForMainDexTracing(appView, subtypingInfo, graphConsumer);
Set<DexProgramClass> liveTypes = enqueuer.traceMainDex(mainDexRootSet, executor, timing);
// LiveTypes is the result.
MainDexClasses mainDexClasses = new MainDexListBuilder(liveTypes, application).run();
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index 50a4a70..cf9684c 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -136,7 +136,7 @@
GraphLense.getIdentityLense(),
PrefixRewritingNamingLens.createPrefixRewritingNamingLens(appView),
null)
- .write(options.getClassFileConsumer(), executor);
+ .write(options.getClassFileConsumer());
options.printWarnings();
} catch (ExecutionException e) {
throw unwrapExecutionException(e);
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 7741651..ff26467 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static com.android.tools.r8.utils.InternalOptions.DETERMINISTIC_DEBUGGING;
+
import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
@@ -189,8 +191,10 @@
new AssertionConfigurationWithDefault(
AssertionTransformation.DISABLE, getAssertionsConfiguration());
- assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
- internal.threadCount = getThreadCount();
+ if (!DETERMINISTIC_DEBUGGING) {
+ assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
+ internal.threadCount = getThreadCount();
+ }
return internal;
}
diff --git a/src/main/java/com/android/tools/r8/L8CommandParser.java b/src/main/java/com/android/tools/r8/L8CommandParser.java
index 90db2c6..0802127 100644
--- a/src/main/java/com/android/tools/r8/L8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/L8CommandParser.java
@@ -139,7 +139,8 @@
builder.error(
new StringDiagnostic("Cannot set multiple " + MIN_API_FLAG + " options", origin));
} else {
- parsePositiveIntArgument(builder, MIN_API_FLAG, nextArg, origin, builder::setMinApiLevel);
+ parsePositiveIntArgument(
+ builder::error, MIN_API_FLAG, nextArg, origin, builder::setMinApiLevel);
hasDefinedApiLevel = true;
}
} else if (arg.equals("--lib")) {
@@ -150,7 +151,7 @@
builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
} else if (arg.equals(THREAD_COUNT_FLAG)) {
parsePositiveIntArgument(
- builder, THREAD_COUNT_FLAG, nextArg, origin, builder::setThreadCount);
+ builder::error, THREAD_COUNT_FLAG, nextArg, origin, builder::setThreadCount);
} else if (arg.startsWith("--")) {
if (!tryParseAssertionArgument(builder, arg, origin)) {
builder.error(new StringDiagnostic("Unknown option: " + arg, origin));
diff --git a/src/main/java/com/android/tools/r8/PrintSeeds.java b/src/main/java/com/android/tools/r8/PrintSeeds.java
index 41c399f..7c2302a 100644
--- a/src/main/java/com/android/tools/r8/PrintSeeds.java
+++ b/src/main/java/com/android/tools/r8/PrintSeeds.java
@@ -6,10 +6,11 @@
import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.EnqueuerFactory;
@@ -86,13 +87,14 @@
try {
DirectMappedDexApplication application =
new ApplicationReader(command.getInputApp(), options, timing).read(executor).toDirect();
- AppView<? extends AppInfoWithSubtyping> appView =
- AppView.createForR8(new AppInfoWithSubtyping(application), options);
+ AppView<? extends AppInfoWithClassHierarchy> appView =
+ AppView.createForR8(new AppInfoWithClassHierarchy(application), options);
appView.setAppServices(AppServices.builder(appView).build());
+ SubtypingInfo subtypingInfo = new SubtypingInfo(application.allClasses(), application);
RootSet rootSet =
- new RootSetBuilder(appView, application, options.getProguardConfiguration().getRules())
+ new RootSetBuilder(appView, subtypingInfo, options.getProguardConfiguration().getRules())
.run(executor);
- Enqueuer enqueuer = EnqueuerFactory.createForInitialTreeShaking(appView);
+ Enqueuer enqueuer = EnqueuerFactory.createForInitialTreeShaking(appView, subtypingInfo);
AppInfoWithLiveness appInfo =
enqueuer.traceApplication(
rootSet, options.getProguardConfiguration().getDontWarnPatterns(), executor, timing);
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java
index 51b6ed7..0b4f28b 100644
--- a/src/main/java/com/android/tools/r8/PrintUses.java
+++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -4,7 +4,7 @@
package com.android.tools.r8;
import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexCallSite;
@@ -74,7 +74,7 @@
private Set<DexType> noObfuscationTypes = Sets.newIdentityHashSet();
private Set<String> keepPackageNames = Sets.newHashSet();
private final DirectMappedDexApplication application;
- private final AppInfoWithSubtyping appInfo;
+ private final AppInfoWithClassHierarchy appInfo;
private int errors;
class UseCollector extends UseRegistry {
@@ -355,7 +355,7 @@
InternalOptions options = new InternalOptions();
application =
new ApplicationReader(inputApp, options, new Timing("PrintUses")).read().toDirect();
- appInfo = new AppInfoWithSubtyping(application);
+ appInfo = new AppInfoWithClassHierarchy(application);
}
private void analyze() {
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 61c4930..30201ce 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -12,7 +12,7 @@
import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.AppliedGraphLens;
@@ -212,7 +212,7 @@
if (options.isGeneratingClassFiles()) {
new CfApplicationWriter(
application, appView, options, marker, graphLense, namingLens, proguardMapSupplier)
- .write(options.getClassFileConsumer(), executorService);
+ .write(options.getClassFileConsumer());
} else {
new ApplicationWriter(
application,
@@ -273,8 +273,8 @@
// Now that the dex-application is fully loaded, close any internal archive providers.
inputApp.closeInternalArchiveProviders();
- AppView<AppInfoWithSubtyping> appView =
- AppView.createForR8(new AppInfoWithSubtyping(application), options);
+ AppView<AppInfoWithClassHierarchy> appView =
+ AppView.createForR8(new AppInfoWithClassHierarchy(application), options);
appView.setAppServices(AppServices.builder(appView).build());
// Up-front check for valid library setup.
@@ -334,11 +334,11 @@
options.itemFactory, AndroidApiLevel.getAndroidApiLevel(options.minApiLevel)));
}
}
-
+ SubtypingInfo subtypingInfo = new SubtypingInfo(application.allClasses(), application);
appView.setRootSet(
new RootSetBuilder(
appView,
- application,
+ subtypingInfo,
Iterables.concat(
options.getProguardConfiguration().getRules(), synthesizedProguardRules))
.run(executorService));
@@ -346,7 +346,7 @@
AnnotationRemover.Builder annotationRemoverBuilder =
options.isShrinking() ? AnnotationRemover.builder() : null;
AppView<AppInfoWithLiveness> appViewWithLiveness =
- runEnqueuer(annotationRemoverBuilder, executorService, appView);
+ runEnqueuer(annotationRemoverBuilder, executorService, appView, subtypingInfo);
application = appViewWithLiveness.appInfo().app().asDirect();
assert appView.rootSet().verifyKeptFieldsAreAccessedAndLive(appViewWithLiveness.appInfo());
assert appView.rootSet().verifyKeptMethodsAreTargetedAndLive(appViewWithLiveness.appInfo());
@@ -414,11 +414,14 @@
if (!options.mainDexKeepRules.isEmpty()) {
assert appView.graphLense().isIdentityLense();
// Find classes which may have code executed before secondary dex files installation.
+ SubtypingInfo subtypingInfo =
+ new SubtypingInfo(appView.appInfo().app().asDirect().allClasses(), appView);
mainDexRootSet =
- new RootSetBuilder(appView, application, options.mainDexKeepRules).run(executorService);
+ new RootSetBuilder(appView, subtypingInfo, options.mainDexKeepRules)
+ .run(executorService);
// Live types is the tracing result.
Set<DexProgramClass> mainDexBaseClasses =
- EnqueuerFactory.createForMainDexTracing(appView)
+ EnqueuerFactory.createForMainDexTracing(appView, subtypingInfo)
.traceMainDex(mainDexRootSet, executorService, timing);
// Calculate the automatic main dex list according to legacy multidex constraints.
mainDexClasses = new MainDexListBuilder(mainDexBaseClasses, application).run();
@@ -469,72 +472,75 @@
new NestReducer(appViewWithLiveness).run(executorService);
timing.end();
}
- if (options.enableHorizontalClassMerging) {
- timing.begin("HorizontalStaticClassMerger");
- StaticClassMerger staticClassMerger =
- new StaticClassMerger(appViewWithLiveness, options, mainDexClasses);
- NestedGraphLense lens = staticClassMerger.run();
- if (lens != null) {
- boolean changed = appView.setGraphLense(lens);
- assert changed;
- appViewWithLiveness.setAppInfo(
- appViewWithLiveness.appInfo().rewrittenWithLens(application.asDirect(), lens));
- }
- timing.end();
- }
- if (options.enableVerticalClassMerging) {
- timing.begin("VerticalClassMerger");
- VerticalClassMerger verticalClassMerger =
- new VerticalClassMerger(
- application, appViewWithLiveness, executorService, timing, mainDexClasses);
- VerticalClassMergerGraphLense lens = verticalClassMerger.run();
- if (lens != null) {
- boolean changed = appView.setGraphLense(lens);
- assert changed;
- appView.setVerticallyMergedClasses(verticalClassMerger.getMergedClasses());
- application = application.asDirect().rewrittenWithLens(lens);
- lens.initializeCacheForLookupMethodInAllContexts();
- appViewWithLiveness.setAppInfo(
- appViewWithLiveness.appInfo().rewrittenWithLens(application.asDirect(), lens));
- lens.unsetCacheForLookupMethodInAllContexts();
- }
- timing.end();
- }
- if (options.enableArgumentRemoval) {
- SubtypingInfo subtypingInfo = appViewWithLiveness.appInfo().computeSubtypingInfo();
- if (options.enableUnusedArgumentRemoval) {
- timing.begin("UnusedArgumentRemoval");
- UnusedArgumentsGraphLense lens =
- new UnusedArgumentsCollector(
- appViewWithLiveness,
- new MethodPoolCollection(appViewWithLiveness, subtypingInfo))
- .run(executorService, timing);
+
+ if (options.getProguardConfiguration().isOptimizing()) {
+ if (options.enableHorizontalClassMerging) {
+ timing.begin("HorizontalStaticClassMerger");
+ StaticClassMerger staticClassMerger =
+ new StaticClassMerger(appViewWithLiveness, options, mainDexClasses);
+ NestedGraphLense lens = staticClassMerger.run();
if (lens != null) {
boolean changed = appView.setGraphLense(lens);
assert changed;
- assert application.asDirect().verifyNothingToRewrite(appView, lens);
appViewWithLiveness.setAppInfo(
appViewWithLiveness.appInfo().rewrittenWithLens(application.asDirect(), lens));
}
timing.end();
}
- if (options.enableUninstantiatedTypeOptimization) {
- timing.begin("UninstantiatedTypeOptimization");
- UninstantiatedTypeOptimizationGraphLense lens =
- new UninstantiatedTypeOptimization(appViewWithLiveness)
- .run(
- new MethodPoolCollection(appViewWithLiveness, subtypingInfo),
- executorService,
- timing);
+ if (options.enableVerticalClassMerging) {
+ timing.begin("VerticalClassMerger");
+ VerticalClassMerger verticalClassMerger =
+ new VerticalClassMerger(
+ application, appViewWithLiveness, executorService, timing, mainDexClasses);
+ VerticalClassMergerGraphLense lens = verticalClassMerger.run();
if (lens != null) {
boolean changed = appView.setGraphLense(lens);
assert changed;
- assert application.asDirect().verifyNothingToRewrite(appView, lens);
+ appView.setVerticallyMergedClasses(verticalClassMerger.getMergedClasses());
+ application = application.asDirect().rewrittenWithLens(lens);
+ lens.initializeCacheForLookupMethodInAllContexts();
appViewWithLiveness.setAppInfo(
appViewWithLiveness.appInfo().rewrittenWithLens(application.asDirect(), lens));
+ lens.unsetCacheForLookupMethodInAllContexts();
}
timing.end();
}
+ if (options.enableArgumentRemoval) {
+ SubtypingInfo subtypingInfo = appViewWithLiveness.appInfo().computeSubtypingInfo();
+ {
+ timing.begin("UnusedArgumentRemoval");
+ UnusedArgumentsGraphLense lens =
+ new UnusedArgumentsCollector(
+ appViewWithLiveness,
+ new MethodPoolCollection(appViewWithLiveness, subtypingInfo))
+ .run(executorService, timing);
+ if (lens != null) {
+ boolean changed = appView.setGraphLense(lens);
+ assert changed;
+ assert application.asDirect().verifyNothingToRewrite(appView, lens);
+ appViewWithLiveness.setAppInfo(
+ appViewWithLiveness.appInfo().rewrittenWithLens(application.asDirect(), lens));
+ }
+ timing.end();
+ }
+ if (options.enableUninstantiatedTypeOptimization) {
+ timing.begin("UninstantiatedTypeOptimization");
+ UninstantiatedTypeOptimizationGraphLense lens =
+ new UninstantiatedTypeOptimization(appViewWithLiveness)
+ .run(
+ new MethodPoolCollection(appViewWithLiveness, subtypingInfo),
+ executorService,
+ timing);
+ if (lens != null) {
+ boolean changed = appView.setGraphLense(lens);
+ assert changed;
+ assert application.asDirect().verifyNothingToRewrite(appView, lens);
+ appViewWithLiveness.setAppInfo(
+ appViewWithLiveness.appInfo().rewrittenWithLens(application.asDirect(), lens));
+ }
+ timing.end();
+ }
+ }
}
// None of the optimizations above should lead to the creation of type lattice elements.
@@ -594,7 +600,7 @@
appViewWithLiveness.appInfo().getEnumValueInfoMapCollection();
if (!options.mainDexKeepRules.isEmpty()) {
- appView.setAppInfo(new AppInfoWithSubtyping(application));
+ appView.setAppInfo(new AppInfoWithClassHierarchy(application));
// No need to build a new main dex root set
assert mainDexRootSet != null;
GraphConsumer mainDexKeptGraphConsumer = options.mainDexKeptGraphConsumer;
@@ -605,7 +611,10 @@
}
Enqueuer enqueuer =
- EnqueuerFactory.createForMainDexTracing(appView, mainDexKeptGraphConsumer);
+ EnqueuerFactory.createForMainDexTracing(
+ appView,
+ new SubtypingInfo(application.allClasses(), application),
+ mainDexKeptGraphConsumer);
// Find classes which may have code executed before secondary dex files installation.
// Live types is the tracing result.
Set<DexProgramClass> mainDexBaseClasses =
@@ -638,7 +647,7 @@
executorService);
}
- appView.setAppInfo(new AppInfoWithSubtyping(application));
+ appView.setAppInfo(new AppInfoWithClassHierarchy(application));
if (options.shouldRerunEnqueuer()) {
timing.begin("Post optimization code stripping");
@@ -654,7 +663,11 @@
}
Enqueuer enqueuer =
- EnqueuerFactory.createForFinalTreeShaking(appView, keptGraphConsumer, missingClasses);
+ EnqueuerFactory.createForFinalTreeShaking(
+ appView,
+ new SubtypingInfo(application.allClasses(), application),
+ keptGraphConsumer,
+ missingClasses);
appView.setAppInfo(
enqueuer
.traceApplication(
@@ -786,8 +799,6 @@
namingLens = NamingLens.getIdentityLens();
}
- ProguardMapSupplier proguardMapSupplier;
-
timing.begin("Line number remapping");
// When line number optimization is turned off the identity mapping for line numbers is
// used. We still run the line number optimizer to collect line numbers and inline frame
@@ -795,7 +806,6 @@
ClassNameMapper classNameMapper =
LineNumberOptimizer.run(appView, application, inputApp, namingLens);
timing.end();
- proguardMapSupplier = ProguardMapSupplier.fromClassNameMapper(classNameMapper, options);
// If a method filter is present don't produce output since the application is likely partial.
if (options.hasMethodsFilter()) {
@@ -846,7 +856,7 @@
appView.initClassLens(),
PrefixRewritingNamingLens.createPrefixRewritingNamingLens(appView, namingLens),
options,
- proguardMapSupplier);
+ ProguardMapSupplier.create(classNameMapper, options));
options.printWarnings();
} catch (ExecutionException e) {
@@ -863,9 +873,10 @@
private AppView<AppInfoWithLiveness> runEnqueuer(
AnnotationRemover.Builder annotationRemoverBuilder,
ExecutorService executorService,
- AppView<AppInfoWithSubtyping> appView)
+ AppView<AppInfoWithClassHierarchy> appView,
+ SubtypingInfo subtypingInfo)
throws ExecutionException {
- Enqueuer enqueuer = EnqueuerFactory.createForInitialTreeShaking(appView);
+ Enqueuer enqueuer = EnqueuerFactory.createForInitialTreeShaking(appView, subtypingInfo);
enqueuer.setAnnotationRemoverBuilder(annotationRemoverBuilder);
if (appView.options().enableInitializedClassesInInstanceMethodsAnalysis) {
enqueuer.registerAnalysis(new InitializedClassesInInstanceMethodsAnalysis(appView));
@@ -897,7 +908,7 @@
RootSet rootSet,
Supplier<Iterable<DexProgramClass>> classes,
WhyAreYouKeepingConsumer whyAreYouKeepingConsumer,
- AppView<? extends AppInfoWithSubtyping> appView,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
Enqueuer enqueuer,
boolean forMainDex,
InternalOptions options,
@@ -921,11 +932,17 @@
// If there is no kept-graph info, re-run the enqueueing to compute it.
if (whyAreYouKeepingConsumer == null) {
whyAreYouKeepingConsumer = new WhyAreYouKeepingConsumer(null);
+ SubtypingInfo subtypingInfo =
+ new SubtypingInfo(appView.appInfo().app().asDirect().allClasses(), appView);
if (forMainDex) {
- enqueuer = EnqueuerFactory.createForMainDexTracing(appView, whyAreYouKeepingConsumer);
+ enqueuer =
+ EnqueuerFactory.createForMainDexTracing(
+ appView, subtypingInfo, whyAreYouKeepingConsumer);
enqueuer.traceMainDex(rootSet, executorService, timing);
} else {
- enqueuer = EnqueuerFactory.createForWhyAreYouKeeping(appView, whyAreYouKeepingConsumer);
+ enqueuer =
+ EnqueuerFactory.createForWhyAreYouKeeping(
+ appView, subtypingInfo, whyAreYouKeepingConsumer);
enqueuer.traceApplication(
rootSet,
options.getProguardConfiguration().getDontWarnPatterns(),
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
index f04f937..d31606d 100644
--- a/src/main/java/com/android/tools/r8/R8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -200,12 +200,12 @@
new StringDiagnostic("Cannot set multiple " + MIN_API_FLAG + " options", argsOrigin));
} else {
parsePositiveIntArgument(
- builder, MIN_API_FLAG, nextArg, argsOrigin, builder::setMinApiLevel);
+ builder::error, MIN_API_FLAG, nextArg, argsOrigin, builder::setMinApiLevel);
state.hasDefinedApiLevel = true;
}
} else if (arg.equals(THREAD_COUNT_FLAG)) {
parsePositiveIntArgument(
- builder, THREAD_COUNT_FLAG, nextArg, argsOrigin, builder::setThreadCount);
+ builder::error, THREAD_COUNT_FLAG, nextArg, argsOrigin, builder::setThreadCount);
} else if (arg.equals("--no-tree-shaking")) {
builder.setDisableTreeShaking(true);
} else if (arg.equals("--no-minification")) {
diff --git a/src/main/java/com/android/tools/r8/StringConsumer.java b/src/main/java/com/android/tools/r8/StringConsumer.java
index 4c091a5..15c2dce 100644
--- a/src/main/java/com/android/tools/r8/StringConsumer.java
+++ b/src/main/java/com/android/tools/r8/StringConsumer.java
@@ -196,7 +196,6 @@
super.accept(string, handler);
try {
writer.write(string);
- writer.flush();
} catch (IOException e) {
handler.error(new ExceptionDiagnostic(e, origin));
}
diff --git a/src/main/java/com/android/tools/r8/SwissArmyKnife.java b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
index 89aa64e..6bf5af9 100644
--- a/src/main/java/com/android/tools/r8/SwissArmyKnife.java
+++ b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.compatproguard.CompatProguard;
import com.android.tools.r8.dexfilemerger.DexFileMerger;
import com.android.tools.r8.dexsplitter.DexSplitter;
+import com.android.tools.r8.relocator.RelocatorCommandLine;
import java.util.Arrays;
/**
@@ -82,6 +83,9 @@
case "backportedmethods":
BackportedMethodList.main(shift(args));
break;
+ case "relocator":
+ RelocatorCommandLine.main(shift((args)));
+ break;
default:
runDefault(args);
break;
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
index ccd1686..87c4b30 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -56,6 +56,7 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.MemberType;
@@ -391,6 +392,9 @@
builder.append(opcodeName(Opcodes.INVOKEDYNAMIC)).append(' ');
builder.append(invoke.getCallSite().methodName);
builder.append(invoke.getCallSite().methodProto.toDescriptorString());
+ DexMethodHandle bootstrapMethod = invoke.getCallSite().bootstrapMethod;
+ builder.append(", bsm:");
+ appendMethod(bootstrapMethod.asMethod());
}
public void print(CfFrame frame) {
diff --git a/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java b/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
index 93973c3..b98c4cb 100644
--- a/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
+++ b/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
@@ -228,7 +228,7 @@
InstructionIterator it = code.instructionIterator();
Instruction instruction = null;
// Set the out-value types of each argument based on the method signature.
- int argumentIndex = code.method.accessFlags.isStatic() ? 0 : -1;
+ int argumentIndex = code.method().accessFlags.isStatic() ? 0 : -1;
while (it.hasNext()) {
instruction = it.next();
if (!instruction.isArgument()) {
@@ -237,12 +237,12 @@
TypeInfo argumentType;
if (argumentIndex < 0) {
argumentType =
- code.method.isInstanceInitializer()
- ? new ThisInstanceInfo(instruction.asArgument(), code.method.holder())
- : createInitializedType(code.method.holder());
+ code.method().isInstanceInitializer()
+ ? new ThisInstanceInfo(instruction.asArgument(), code.method().holder())
+ : createInitializedType(code.method().holder());
} else {
argumentType =
- createInitializedType(code.method.method.proto.parameters.values[argumentIndex]);
+ createInitializedType(code.method().method.proto.parameters.values[argumentIndex]);
}
Value outValue = instruction.outValue();
if (outValue.outType().isObject()) {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
index 8f01356..458bc23 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
@@ -62,7 +62,8 @@
case METHOD_TYPE:
return Type.getMethodType(value.asDexValueMethodType().getValue().toDescriptorString(lens));
case STRING:
- return value.asDexValueString().getValue();
+ DexString innerValue = value.asDexValueString().getValue();
+ return innerValue == null ? null : innerValue.toString();
case TYPE:
return Type.getType(lens.lookupDescriptor(value.asDexValueType().value).toString());
default:
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 79b4362..8ee86f3 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -38,9 +38,9 @@
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.graph.ParameterAnnotationsList;
-import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.naming.ProguardMapSupplier;
+import com.android.tools.r8.naming.ProguardMapSupplier.ProguardMapId;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.ExceptionUtils;
@@ -230,17 +230,17 @@
public void write(ExecutorService executorService) throws IOException, ExecutionException {
application.timing.begin("DexApplication.write");
- ProguardMapSupplier.ProguardMapAndId proguardMapAndId = null;
+ ProguardMapId proguardMapId = null;
if (proguardMapSupplier != null && options.proguardMapConsumer != null) {
- proguardMapAndId = proguardMapSupplier.getProguardMapAndId();
+ proguardMapId = proguardMapSupplier.writeProguardMap();
}
// If we do have a map then we're called from R8. In that case we have exactly one marker.
- assert proguardMapAndId == null || (markers != null && markers.size() == 1);
+ assert proguardMapId == null || (markers != null && markers.size() == 1);
if (markers != null && !markers.isEmpty()) {
- if (proguardMapAndId != null) {
- markers.get(0).setPgMapId(proguardMapAndId.id);
+ if (proguardMapId != null) {
+ markers.get(0).setPgMapId(proguardMapId.get());
}
markerStrings = new ArrayList<>(markers.size());
for (Marker marker : markers) {
@@ -338,13 +338,7 @@
// Fail if there are pending errors, e.g., the program consumers may have reported errors.
options.reporter.failIfPendingErrors();
// Supply info to all additional resource consumers.
- supplyAdditionalConsumers(
- application,
- appView,
- graphLense,
- namingLens,
- options,
- proguardMapAndId == null ? null : proguardMapAndId.map);
+ supplyAdditionalConsumers(application, appView, graphLense, namingLens, options);
} finally {
application.timing.end();
}
@@ -355,20 +349,13 @@
AppView<?> appView,
GraphLense graphLense,
NamingLens namingLens,
- InternalOptions options,
- String proguardMapContent) {
+ InternalOptions options) {
if (options.configurationConsumer != null) {
ExceptionUtils.withConsumeResourceHandler(
options.reporter, options.configurationConsumer,
options.getProguardConfiguration().getParsedConfiguration());
ExceptionUtils.withFinishedResourceHandler(options.reporter, options.configurationConsumer);
}
- if (proguardMapContent != null) {
- assert validateProguardMapParses(proguardMapContent);
- ExceptionUtils.withConsumeResourceHandler(
- options.reporter, options.proguardMapConsumer, proguardMapContent);
- ExceptionUtils.withFinishedResourceHandler(options.reporter, options.proguardMapConsumer);
- }
if (options.mainDexListConsumer != null) {
ExceptionUtils.withConsumeResourceHandler(
options.reporter, options.mainDexListConsumer, writeMainDexList(application, namingLens));
@@ -465,16 +452,6 @@
}
}
- private static boolean validateProguardMapParses(String content) {
- try {
- ClassNameMapper.mapperFromString(content);
- } catch (IOException e) {
- e.printStackTrace();
- return false;
- }
- return true;
- }
-
private void insertAttributeAnnotations() {
// Convert inner-class attributes to DEX annotations
for (DexProgramClass clazz : application.classes()) {
diff --git a/src/main/java/com/android/tools/r8/dex/Marker.java b/src/main/java/com/android/tools/r8/dex/Marker.java
index 0878119..1484f02 100644
--- a/src/main/java/com/android/tools/r8/dex/Marker.java
+++ b/src/main/java/com/android/tools/r8/dex/Marker.java
@@ -5,30 +5,36 @@
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.StringDiagnostic;
+import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import java.util.Comparator;
+import java.util.HashSet;
import java.util.Map.Entry;
+import java.util.Set;
-/**
- * Abstraction for hidden dex marker intended for the main dex file.
- */
+/** Abstraction for hidden dex marker intended for the main dex file. */
public class Marker {
public static final String VERSION = "version";
public static final String MIN_API = "min-api";
+ public static final String DESUGARED_LIBRARY_IDENTIFIERS = "desugared-library-identifiers";
public static final String SHA1 = "sha-1";
public static final String COMPILATION_MODE = "compilation-mode";
public static final String HAS_CHECKSUMS = "has-checksums";
public static final String PG_MAP_ID = "pg-map-id";
public static final String R8_MODE = "r8-mode";
+ private static final String NO_LIBRARY_DESUGARING = "<no-library-desugaring>";
public enum Tool {
D8,
R8,
- L8;
+ L8,
+ Relocator;
public static Tool[] valuesR8andD8() {
return new Tool[] {Tool.D8, Tool.R8};
@@ -53,6 +59,48 @@
this.jsonObject = jsonObject;
}
+ public static void checkCompatibleDesugaredLibrary(Set<Marker> markers, Reporter reporter) {
+ if (markers.size() <= 1) {
+ return;
+ }
+ // In L8 compilation, the compilation has two markers, a L8 marker, which has a desugared
+ // library property, and either a D8 or a R8 marker, which has no desugared library property.
+ // In other compilations, the desugared library versions have to be consistent.
+ Set<String> desugaredLibraryIdentifiers = new HashSet<>();
+ for (Marker marker : markers) {
+ if (marker.tool == Tool.L8) {
+ assert marker.getDesugaredLibraryIdentifiers().length > 0;
+ assert markers.stream()
+ .allMatch(m -> m.tool == Tool.L8 || m.getDesugaredLibraryIdentifiers().length == 0);
+ } else {
+ String[] identifiers = marker.getDesugaredLibraryIdentifiers();
+ String identifier;
+ switch (identifiers.length) {
+ case 0:
+ identifier = NO_LIBRARY_DESUGARING;
+ break;
+ case 1:
+ identifier = identifiers[0];
+ break;
+ default:
+ // To be implemented once D8/R8 compilation supports multiple desugared libraries.
+ throw reporter.fatalError(
+ new StringDiagnostic(
+ "Merging program compiled with multiple desugared libraries."));
+ }
+ desugaredLibraryIdentifiers.add(identifier);
+ }
+ }
+
+ if (desugaredLibraryIdentifiers.size() > 1) {
+ reporter.error(
+ new StringDiagnostic(
+ "The compilation is merging inputs with different desugared library desugaring "
+ + desugaredLibraryIdentifiers
+ + ", which may lead to unexpected runtime errors."));
+ }
+ }
+
public Tool getTool() {
return tool;
}
@@ -69,6 +117,10 @@
return tool == Tool.L8;
}
+ public boolean isRelocator() {
+ return tool == Tool.Relocator;
+ }
+
public String getVersion() {
return jsonObject.get(VERSION).getAsString();
}
@@ -89,6 +141,28 @@
return this;
}
+ public String[] getDesugaredLibraryIdentifiers() {
+ if (jsonObject.has(DESUGARED_LIBRARY_IDENTIFIERS)) {
+ JsonArray array = jsonObject.get(DESUGARED_LIBRARY_IDENTIFIERS).getAsJsonArray();
+ String[] identifiers = new String[array.size()];
+ for (int i = 0; i < array.size(); i++) {
+ identifiers[i] = array.get(i).getAsString();
+ }
+ return identifiers;
+ }
+ return new String[0];
+ }
+
+ public Marker setDesugaredLibraryIdentifiers(String... identifiers) {
+ assert !jsonObject.has(DESUGARED_LIBRARY_IDENTIFIERS);
+ JsonArray jsonIdentifiers = new JsonArray();
+ for (String identifier : identifiers) {
+ jsonIdentifiers.add(identifier);
+ }
+ jsonObject.add(DESUGARED_LIBRARY_IDENTIFIERS, jsonIdentifiers);
+ return this;
+ }
+
public String getSha1() {
return jsonObject.get(SHA1).getAsString();
}
@@ -143,8 +217,7 @@
public String toString() {
// In order to make printing of markers deterministic we sort the entries by key.
final JsonObject sortedJson = new JsonObject();
- jsonObject.entrySet()
- .stream()
+ jsonObject.entrySet().stream()
.sorted(Comparator.comparing(Entry::getKey))
.forEach(entry -> sortedJson.add(entry.getKey(), entry.getValue()));
return PREFIX + tool + sortedJson;
@@ -186,7 +259,7 @@
private static Marker internalParse(Tool tool, String str) {
try {
- JsonElement result = new JsonParser().parse(str);
+ JsonElement result = new JsonParser().parse(str);
if (result.isJsonObject()) {
return new Marker(tool, result.getAsJsonObject());
}
diff --git a/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java b/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java
index a122ad7..4a4db11 100644
--- a/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java
+++ b/src/main/java/com/android/tools/r8/features/FeatureSplitConfiguration.java
@@ -107,11 +107,21 @@
return result;
}
+ public boolean inBaseOrSameFeatureAs(DexProgramClass clazz, DexProgramClass context) {
+ FeatureSplit split = javaTypeToFeatureSplitMapping.get(clazz.type.toSourceString());
+ return split == null
+ || split == javaTypeToFeatureSplitMapping.get(context.type.toSourceString());
+ }
+
public boolean isInFeature(DexProgramClass clazz) {
return javaTypeToFeatureSplitMapping.containsKey(
DescriptorUtils.descriptorToJavaType(clazz.type.toDescriptorString()));
}
+ public boolean isInBase(DexProgramClass clazz) {
+ return !isInFeature(clazz);
+ }
+
public boolean inSameFeatureOrBase(DexMethod a, DexMethod b){
return inSameFeatureOrBase(a.holder, b.holder);
}
diff --git a/src/main/java/com/android/tools/r8/graph/AccessControl.java b/src/main/java/com/android/tools/r8/graph/AccessControl.java
index d775cb3..2c05df2 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessControl.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessControl.java
@@ -3,6 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import com.android.tools.r8.features.FeatureSplitConfiguration;
+import com.android.tools.r8.utils.OptionalBool;
+
/**
* Definitions of access control routines.
*
@@ -11,14 +14,22 @@
*/
public class AccessControl {
- public static boolean isClassAccessible(DexClass clazz, DexProgramClass context) {
- if (clazz.accessFlags.isPublic()) {
- return true;
+ public static OptionalBool isClassAccessible(
+ DexClass clazz,
+ DexProgramClass context,
+ FeatureSplitConfiguration featureSplitConfiguration) {
+ if (!clazz.isPublic() && !clazz.getType().isSamePackage(context.getType())) {
+ return OptionalBool.FALSE;
}
- return clazz.getType().isSamePackage(context.getType());
+ if (featureSplitConfiguration != null
+ && clazz.isProgramClass()
+ && !featureSplitConfiguration.inBaseOrSameFeatureAs(clazz.asProgramClass(), context)) {
+ return OptionalBool.UNKNOWN;
+ }
+ return OptionalBool.TRUE;
}
- public static boolean isMethodAccessible(
+ public static OptionalBool isMethodAccessible(
DexEncodedMethod method,
DexClass holder,
DexProgramClass context,
@@ -26,35 +37,40 @@
return isMemberAccessible(method.accessFlags, holder, context, appInfo);
}
- public static boolean isFieldAccessible(
+ public static OptionalBool isFieldAccessible(
DexEncodedField field,
DexClass holder,
DexProgramClass context,
- AppInfoWithSubtyping appInfo) {
+ AppInfoWithClassHierarchy appInfo) {
return isMemberAccessible(field.accessFlags, holder, context, appInfo);
}
- private static boolean isMemberAccessible(
+ private static OptionalBool isMemberAccessible(
AccessFlags<?> memberFlags,
DexClass holder,
DexProgramClass context,
AppInfoWithClassHierarchy appInfo) {
- if (!isClassAccessible(holder, context)) {
- return false;
+ OptionalBool classAccessibility =
+ isClassAccessible(holder, context, appInfo.options().featureSplitConfiguration);
+ if (classAccessibility.isFalse()) {
+ return OptionalBool.FALSE;
}
if (memberFlags.isPublic()) {
- return true;
+ return classAccessibility;
}
if (memberFlags.isPrivate()) {
- return isNestMate(holder, context);
+ if (!isNestMate(holder, context)) {
+ return OptionalBool.FALSE;
+ }
+ return classAccessibility;
}
if (holder.getType().isSamePackage(context.getType())) {
- return true;
+ return classAccessibility;
}
- if (!memberFlags.isProtected()) {
- return false;
+ if (memberFlags.isProtected() && appInfo.isSubtype(context.getType(), holder.getType())) {
+ return classAccessibility;
}
- return appInfo.isSubtype(context.getType(), holder.getType());
+ return OptionalBool.FALSE;
}
private static boolean isNestMate(DexClass clazz, DexProgramClass context) {
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index 8b63cbf..ef53aaa 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -578,16 +578,6 @@
return null;
}
- public boolean hasSubtyping() {
- assert checkIfObsolete();
- return false;
- }
-
- public AppInfoWithSubtyping withSubtyping() {
- assert checkIfObsolete();
- return null;
- }
-
public boolean hasLiveness() {
assert checkIfObsolete();
return false;
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
deleted file mode 100644
index 794745f..0000000
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.graph;
-
-import java.util.Collection;
-
-public class AppInfoWithSubtyping extends AppInfoWithClassHierarchy {
-
- public AppInfoWithSubtyping(DirectMappedDexApplication application) {
- this(application, application.allClasses());
- }
-
- public AppInfoWithSubtyping(
- DirectMappedDexApplication application, Collection<DexClass> classes) {
- super(application);
- }
-
- protected AppInfoWithSubtyping(AppInfoWithSubtyping previous) {
- super(previous);
- assert app() instanceof DirectMappedDexApplication;
- }
-
- private DirectMappedDexApplication getDirectApplication() {
- // TODO(herhut): Remove need for cast.
- return (DirectMappedDexApplication) app();
- }
-
- public Iterable<DexLibraryClass> libraryClasses() {
- assert checkIfObsolete();
- return getDirectApplication().libraryClasses();
- }
-
- @Override
- public boolean hasSubtyping() {
- assert checkIfObsolete();
- return true;
- }
-
- @Override
- public AppInfoWithSubtyping withSubtyping() {
- assert checkIfObsolete();
- return this;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index ef76217..484c3e1 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -130,6 +130,11 @@
return new AppView<>(appInfo, WholeProgramOptimizations.OFF, options, mapper);
}
+ public static <T extends AppInfo> AppView<T> createForRelocator(
+ T appInfo, InternalOptions options) {
+ return new AppView<>(appInfo, WholeProgramOptimizations.OFF, options);
+ }
+
public AbstractValueFactory abstractValueFactory() {
return abstractValueFactory;
}
@@ -418,13 +423,6 @@
: null;
}
- @SuppressWarnings("unchecked")
- public AppView<AppInfoWithSubtyping> withSubtyping() {
- return appInfo.hasSubtyping()
- ? (AppView<AppInfoWithSubtyping>) this
- : null;
- }
-
public AppView<AppInfoWithLiveness> withLiveness() {
@SuppressWarnings("unchecked")
AppView<AppInfoWithLiveness> appViewWithLiveness = (AppView<AppInfoWithLiveness>) this;
@@ -432,8 +430,8 @@
}
public OptionalBool isSubtype(DexType subtype, DexType supertype) {
- return appInfo().hasSubtyping()
- ? OptionalBool.of(appInfo().withSubtyping().isSubtype(subtype, supertype))
+ return appInfo().hasLiveness()
+ ? OptionalBool.of(appInfo().withLiveness().isSubtype(subtype, supertype))
: OptionalBool.unknown();
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
index a05883a..158bf1f 100644
--- a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
@@ -28,7 +28,7 @@
new IdentityHashMap<>();
public AppliedGraphLens(
- AppView<? extends AppInfoWithSubtyping> appView, Iterable<DexProgramClass> classes) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, Iterable<DexProgramClass> classes) {
this.appView = appView;
for (DexProgramClass clazz : classes) {
diff --git a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
index 968b1c9..59b3c8b 100644
--- a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
@@ -10,8 +10,7 @@
import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.kotlin.KotlinClassMetadataReader;
-import com.android.tools.r8.kotlin.KotlinInfo;
+import com.android.tools.r8.kotlin.KotlinMetadataWriter;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.utils.CfgPrinter;
@@ -28,7 +27,7 @@
private final boolean writeFields;
private final boolean writeAnnotations;
private final boolean writeIR;
- private final AppInfoWithSubtyping appInfo;
+ private final AppInfoWithClassHierarchy appInfo;
private final Kotlin kotlin;
private final Timing timing = new Timing("AssemblyWriter");
@@ -40,7 +39,7 @@
this.writeAnnotations = allInfo;
this.writeIR = writeIR;
if (writeIR) {
- this.appInfo = new AppInfoWithSubtyping(application.toDirect());
+ this.appInfo = new AppInfoWithClassHierarchy(application.toDirect());
if (options.programConsumer == null) {
// Use class-file backend, since the CF frontend for testing does not support desugaring of
// synchronized methods for the DEX backend (b/109789541).
@@ -149,13 +148,13 @@
if (writeAnnotations) {
if (!annotations.isEmpty()) {
ps.println("# Annotations:");
+ String prefix = "# ";
for (DexAnnotation annotation : annotations.annotations) {
if (annotation.annotation.type == kotlin.metadata.kotlinMetadataType) {
assert clazz != null : "Kotlin metadata is a class annotation";
- writeKotlinMetadata(clazz, annotation, ps);
+ KotlinMetadataWriter.writeKotlinMetadataAnnotation(prefix, annotation, ps, kotlin);
} else {
String annotationString = annotation.toString();
- String prefix = "# ";
ps.print(
new BufferedReader(new StringReader(annotationString))
.lines()
@@ -168,14 +167,6 @@
}
}
- private void writeKotlinMetadata(
- DexProgramClass clazz, DexAnnotation annotation, PrintStream ps) {
- assert annotation.annotation.type == kotlin.metadata.kotlinMetadataType;
- KotlinInfo kotlinInfo =
- KotlinClassMetadataReader.createKotlinInfo(kotlin, clazz, annotation.annotation);
- ps.println(kotlinInfo.toString("# "));
- }
-
@Override
void writeClassFooter(DexProgramClass clazz, PrintStream ps) {
diff --git a/src/main/java/com/android/tools/r8/graph/BottomUpClassHierarchyTraversal.java b/src/main/java/com/android/tools/r8/graph/BottomUpClassHierarchyTraversal.java
index 9cdac51..1c7bf88 100644
--- a/src/main/java/com/android/tools/r8/graph/BottomUpClassHierarchyTraversal.java
+++ b/src/main/java/com/android/tools/r8/graph/BottomUpClassHierarchyTraversal.java
@@ -10,7 +10,9 @@
private final SubtypingInfo subtypingInfo;
private BottomUpClassHierarchyTraversal(
- AppView<? extends AppInfoWithSubtyping> appView, SubtypingInfo subtypingInfo, Scope scope) {
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ SubtypingInfo subtypingInfo,
+ Scope scope) {
super(appView, scope);
this.subtypingInfo = subtypingInfo;
}
@@ -20,7 +22,7 @@
* classes) that are reachable from a given set of sources.
*/
public static BottomUpClassHierarchyTraversal<DexClass> forAllClasses(
- AppView<? extends AppInfoWithSubtyping> appView, SubtypingInfo subtypingInfo) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, SubtypingInfo subtypingInfo) {
return new BottomUpClassHierarchyTraversal<>(appView, subtypingInfo, Scope.ALL_CLASSES);
}
@@ -29,7 +31,7 @@
* given set of sources.
*/
public static BottomUpClassHierarchyTraversal<DexProgramClass> forProgramClasses(
- AppView<? extends AppInfoWithSubtyping> appView, SubtypingInfo subtypingInfo) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, SubtypingInfo subtypingInfo) {
return new BottomUpClassHierarchyTraversal<>(
appView, subtypingInfo, Scope.ONLY_PROGRAM_CLASSES);
}
diff --git a/src/main/java/com/android/tools/r8/graph/ClassHierarchyTraversal.java b/src/main/java/com/android/tools/r8/graph/ClassHierarchyTraversal.java
index 550a96c..83b09c3 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassHierarchyTraversal.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassHierarchyTraversal.java
@@ -41,7 +41,7 @@
}
}
- final AppView<? extends AppInfoWithSubtyping> appView;
+ final AppView<? extends AppInfoWithClassHierarchy> appView;
final Scope scope;
final Set<DexClass> visited = new HashSet<>();
@@ -49,7 +49,7 @@
boolean excludeInterfaces = false;
- ClassHierarchyTraversal(AppView<? extends AppInfoWithSubtyping> appView, Scope scope) {
+ ClassHierarchyTraversal(AppView<? extends AppInfoWithClassHierarchy> appView, Scope scope) {
this.appView = appView;
this.scope = scope;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCallSite.java b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
index 3249abf..8c76ca1 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCallSite.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
import com.android.tools.r8.graph.DexValue.DexValueMethodType;
@@ -68,7 +69,7 @@
if (bsmHandle.getTag() != Opcodes.H_INVOKESTATIC
&& bsmHandle.getTag() != Opcodes.H_NEWINVOKESPECIAL) {
// JVM9 §4.7.23 note: Tag must be InvokeStatic or NewInvokeSpecial.
- throw new Unreachable("Bootstrap handle invalid: tag == " + bsmHandle.getTag());
+ throw new CompilationError("Bootstrap handle invalid: tag == " + bsmHandle.getTag());
}
// Resolve the bootstrap method.
DexMethodHandle bootstrapMethod = DexMethodHandle.fromAsmHandle(bsmHandle, application, clazz);
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 32e1d61..444459f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.kotlin.KotlinInfo;
+import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OptionalBool;
@@ -794,11 +794,7 @@
}
/** Returns kotlin class info if the class is synthesized by kotlin compiler. */
- public abstract KotlinInfo getKotlinInfo();
-
- public final boolean hasKotlinInfo() {
- return getKotlinInfo() != null;
- }
+ public abstract KotlinClassLevelInfo getKotlinInfo();
public boolean hasInstanceFields() {
return instanceFields.length > 0;
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
index 6c05f12..f77d55b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -49,7 +49,7 @@
return holder;
}
- public DexEncodedMethod getMethod() {
+ public DexEncodedMethod getDefinition() {
return method;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
index ba47985..1e85690 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
@@ -8,7 +8,7 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.kotlin.KotlinInfo;
+import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
import com.android.tools.r8.origin.Origin;
import java.util.List;
import java.util.function.Supplier;
@@ -86,8 +86,8 @@
}
@Override
- public KotlinInfo getKotlinInfo() {
- throw new Unreachable("Kotlin into n classpath class is not supported yet.");
+ public KotlinClassLevelInfo getKotlinInfo() {
+ throw new Unreachable("Kotlin info on classpath class is not supported yet.");
}
@Override
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 53d2835..bb3405a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -59,7 +59,7 @@
private int startLine = NO_LINE_INFO;
public DexDebugEventBuilder(IRCode code, InternalOptions options) {
- this.method = code.method;
+ this.method = code.method();
this.factory = options.itemFactory;
this.options = options;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 0d5c50d..43536b9 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.graph;
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
@@ -16,7 +17,7 @@
import com.android.tools.r8.ir.optimize.info.DefaultFieldOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.MutableFieldOptimizationInfo;
-import com.android.tools.r8.kotlin.KotlinMemberInfo;
+import com.android.tools.r8.kotlin.KotlinFieldLevelInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.Sets;
@@ -28,7 +29,7 @@
private DexValue staticValue;
private FieldOptimizationInfo optimizationInfo = DefaultFieldOptimizationInfo.getInstance();
- private KotlinMemberInfo kotlinMemberInfo = KotlinMemberInfo.getNoKotlinMemberInfo();
+ private KotlinFieldLevelInfo kotlinMemberInfo = NO_KOTLIN_INFO;
public DexEncodedField(
DexField field,
@@ -67,23 +68,15 @@
optimizationInfo = info;
}
- public KotlinMemberInfo getKotlinMemberInfo() {
+ public KotlinFieldLevelInfo getKotlinMemberInfo() {
return kotlinMemberInfo;
}
- public void setKotlinMemberInfo(KotlinMemberInfo kotlinMemberInfo) {
- assert this.kotlinMemberInfo == KotlinMemberInfo.getNoKotlinMemberInfo();
+ public void setKotlinMemberInfo(KotlinFieldLevelInfo kotlinMemberInfo) {
+ assert this.kotlinMemberInfo == NO_KOTLIN_INFO;
this.kotlinMemberInfo = kotlinMemberInfo;
}
- public boolean isKotlinBackingField() {
- return kotlinMemberInfo.memberKind.isBackingField();
- }
-
- public boolean isKotlinBackingFieldForCompanionObject() {
- return kotlinMemberInfo.memberKind.isBackingFieldForCompanionObject();
- }
-
@Override
public void collectIndexedItems(
IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
@@ -129,6 +122,10 @@
return this;
}
+ public boolean isEnum() {
+ return accessFlags.isEnum();
+ }
+
public boolean isFinal() {
return accessFlags.isFinal();
}
@@ -210,7 +207,7 @@
&& singleValue.asSingleFieldValue().getField() == field) {
return null;
}
- if (singleValue.isMaterializableInContext(appView, code.method.holder())) {
+ if (singleValue.isMaterializableInContext(appView, code.method().holder())) {
TypeElement type = TypeElement.fromDexType(field.type, maybeNull(), appView);
return singleValue.createMaterializingInstruction(
appView, code, TypeAndLocalInfoSupplier.create(type, local));
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 28a0da2..816a515 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -9,6 +9,7 @@
import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_INLINING_CANDIDATE_SAME_PACKAGE;
import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_INLINING_CANDIDATE_SUBCLASS;
import static com.android.tools.r8.graph.DexEncodedMethod.CompilationState.PROCESSED_NOT_INLINING_CANDIDATE;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
import com.android.tools.r8.cf.code.CfConstNull;
import com.android.tools.r8.cf.code.CfConstString;
@@ -55,7 +56,7 @@
import com.android.tools.r8.ir.synthetic.FieldAccessorSourceCode;
import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
import com.android.tools.r8.ir.synthetic.SynthesizedCode;
-import com.android.tools.r8.kotlin.KotlinMemberInfo;
+import com.android.tools.r8.kotlin.KotlinMethodLevelInfo;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -139,7 +140,7 @@
private MethodOptimizationInfo optimizationInfo = DefaultMethodOptimizationInfo.DEFAULT_INSTANCE;
private CallSiteOptimizationInfo callSiteOptimizationInfo = CallSiteOptimizationInfo.BOTTOM;
private int classFileVersion;
- private KotlinMemberInfo kotlinMemberInfo = KotlinMemberInfo.getNoKotlinMemberInfo();
+ private KotlinMethodLevelInfo kotlinMemberInfo = NO_KOTLIN_INFO;
private DexEncodedMethod defaultInterfaceMethodImplementation = null;
@@ -256,6 +257,10 @@
assert parameterAnnotationsList != null;
}
+ public DexTypeList parameters() {
+ return method.proto.parameters;
+ }
+
public DexType returnType() {
return method.proto.returnType;
}
@@ -412,42 +417,30 @@
return accessFlags.isSynthetic();
}
- public KotlinMemberInfo getKotlinMemberInfo() {
+ public KotlinMethodLevelInfo getKotlinMemberInfo() {
return kotlinMemberInfo;
}
- public void setKotlinMemberInfo(KotlinMemberInfo kotlinMemberInfo) {
- if (this.kotlinMemberInfo == KotlinMemberInfo.getNoKotlinMemberInfo()) {
- // Initial setup or structure-changing optimizations that just need to copy metadata from the
- // old instance of DexEncodedMethod to the new one.
- this.kotlinMemberInfo = kotlinMemberInfo;
- } else {
- // Structure-changing optimizations, such as (vertical|horizontal) merger or inliner, that
- // may need to redefine what this method is. Simply, the method merged/inlined by optimization
- // is no longer what it used to be; it's safe to ignore metadata of that method, since it is
- // not asked to be kept. But, the nature of the current one is not changed, hence keeping the
- // original one as-is.
- // E.g., originally the current method is extension function, and new information, say, from
- // an inlinee, is extension property. Being merged here means:
- // * That inlinee is not an extension property anymore. We can ignore metadata from it.
- // * This method is still an extension function, just with a bigger body.
- }
+ public void setKotlinMemberInfo(KotlinMethodLevelInfo kotlinMemberInfo) {
+ // Structure-changing optimizations, such as (vertical|horizontal) merger or inliner, that
+ // may need to redefine what this method is. Simply, the method merged/inlined by optimization
+ // is no longer what it used to be; it's safe to ignore metadata of that method, since it is
+ // not asked to be kept. But, the nature of the current one is not changed, hence keeping the
+ // original one as-is.
+ // E.g., originally the current method is extension function, and new information, say, from
+ // an inlinee, is extension property. Being merged here means:
+ // * That inlinee is not an extension property anymore. We can ignore metadata from it.
+ // * This method is still an extension function, just with a bigger body.
+ assert this.kotlinMemberInfo == NO_KOTLIN_INFO;
+ this.kotlinMemberInfo = kotlinMemberInfo;
}
public boolean isKotlinFunction() {
- return kotlinMemberInfo.memberKind.isFunction();
+ return kotlinMemberInfo.isFunction();
}
public boolean isKotlinExtensionFunction() {
- return kotlinMemberInfo.memberKind.isExtensionFunction();
- }
-
- public boolean isKotlinProperty() {
- return kotlinMemberInfo.memberKind.isProperty();
- }
-
- public boolean isKotlinExtensionProperty() {
- return kotlinMemberInfo.memberKind.isExtensionProperty();
+ return kotlinMemberInfo.isFunction() && kotlinMemberInfo.asFunction().isExtensionFunction();
}
public boolean isOnlyInlinedIntoNestMembers() {
@@ -457,7 +450,7 @@
public boolean isInliningCandidate(
DexEncodedMethod container,
Reason inliningReason,
- AppInfoWithSubtyping appInfo,
+ AppInfoWithClassHierarchy appInfo,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
checkIfObsolete();
return isInliningCandidate(
@@ -467,7 +460,7 @@
public boolean isInliningCandidate(
DexType containerType,
Reason inliningReason,
- AppInfoWithSubtyping appInfo,
+ AppInfoWithClassHierarchy appInfo,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
checkIfObsolete();
@@ -1250,7 +1243,6 @@
public void copyMetadata(DexEncodedMethod from) {
checkIfObsolete();
- setKotlinMemberInfo(from.kotlinMemberInfo);
if (from.classFileVersion > classFileVersion) {
upgradeClassFileVersion(from.getClassFileVersion());
}
@@ -1273,7 +1265,7 @@
private Code code;
private CompilationState compilationState;
private MethodOptimizationInfo optimizationInfo;
- private KotlinMemberInfo kotlinMemberInfo;
+ private KotlinMethodLevelInfo kotlinMemberInfo;
private final int classFileVersion;
private boolean d8R8Synthesized;
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 ffa702a..7275598 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -434,7 +434,8 @@
public final DexType nestConstructorType = createStaticallyKnownType(nestConstructorDescriptor);
public final DexString enumUnboxingUtilityDescriptor =
- createString("L" + EnumUnboxingRewriter.ENUM_UNBOXING_UTILITY_CLASS_NAME + ";");
+ createString(
+ "Lcom/android/tools/r8/" + EnumUnboxingRewriter.ENUM_UNBOXING_UTILITY_CLASS_NAME + ";");
public final DexType enumUnboxingUtilityType =
createStaticallyKnownType(enumUnboxingUtilityDescriptor);
@@ -2086,7 +2087,7 @@
// Don't reason at the level of interfaces in D8.
return ClassTypeElement.create(type, nullability, Collections.emptySet());
}
- assert appView.appInfo().hasSubtyping();
+ assert appView.appInfo().hasClassHierarchy();
if (appView.isInterface(type).isTrue()) {
return ClassTypeElement.create(
objectType, nullability, Collections.singleton(type));
@@ -2096,7 +2097,7 @@
// and compute the least upper bound of two interface sets. Hence, lazy
// computations. Most likely during lattice join. See {@link
// ClassTypeElement#getInterfaces}.
- return ClassTypeElement.create(type, nullability, appView.withSubtyping());
+ return ClassTypeElement.create(type, nullability, appView.withClassHierarchy());
}
assert type.isArrayType();
return ArrayTypeElement.create(finalMemberType, nullability);
@@ -2105,7 +2106,7 @@
}
public Set<DexType> getOrComputeLeastUpperBoundOfImplementedInterfaces(
- DexType type, AppView<? extends AppInfoWithSubtyping> appView) {
+ DexType type, AppView<? extends AppInfoWithClassHierarchy> appView) {
return classTypeInterfaces.computeIfAbsent(
type,
t -> {
diff --git a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
index 4b0e1fb..35d8146 100644
--- a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
@@ -8,7 +8,7 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.kotlin.KotlinInfo;
+import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
import com.android.tools.r8.origin.Origin;
import java.util.Arrays;
import java.util.List;
@@ -114,8 +114,8 @@
}
@Override
- public KotlinInfo getKotlinInfo() {
- return null;
+ public KotlinClassLevelInfo getKotlinInfo() {
+ throw new Unreachable("We should never consider metadata for library classes");
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 9085643..bda291b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -3,12 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
+
import com.android.tools.r8.ProgramResource;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.kotlin.KotlinInfo;
+import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -36,7 +38,7 @@
private final ProgramResource.Kind originKind;
private final Collection<DexProgramClass> synthesizedFrom;
private int initialClassFileVersion = -1;
- private KotlinInfo kotlinInfo = null;
+ private KotlinClassLevelInfo kotlinInfo = NO_KOTLIN_INFO;
private final ChecksumSupplier checksumSupplier;
@@ -252,12 +254,13 @@
}
@Override
- public KotlinInfo getKotlinInfo() {
+ public KotlinClassLevelInfo getKotlinInfo() {
return kotlinInfo;
}
- public void setKotlinInfo(KotlinInfo kotlinInfo) {
- assert this.kotlinInfo == null || kotlinInfo == null;
+ public void setKotlinInfo(KotlinClassLevelInfo kotlinInfo) {
+ assert kotlinInfo != null;
+ assert this.kotlinInfo == NO_KOTLIN_INFO || kotlinInfo == NO_KOTLIN_INFO;
this.kotlinInfo = kotlinInfo;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeList.java b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
index 58b6afd..2272c98 100644
--- a/src/main/java/com/android/tools/r8/graph/DexTypeList.java
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.ArrayUtils;
import java.util.Arrays;
public class DexTypeList extends DexItem {
@@ -28,6 +29,10 @@
this.values = values;
}
+ public boolean contains(DexType type) {
+ return ArrayUtils.contains(values, type);
+ }
+
@Override
public int hashCode() {
return Arrays.hashCode(values);
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
index bab3c75..96b6b5a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -11,6 +11,9 @@
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.Unreachable;
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.AbstractValueFactory;
+import com.android.tools.r8.ir.analysis.value.UnknownValue;
import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
import com.android.tools.r8.ir.code.ConstInstruction;
import com.android.tools.r8.ir.code.ConstString;
@@ -264,6 +267,8 @@
return null;
}
+ public abstract AbstractValue toAbstractValue(AbstractValueFactory factory);
+
static DexValue fromAsmBootstrapArgument(
Object value, JarApplicationReader application, DexType clazz) {
if (value instanceof Integer) {
@@ -352,7 +357,7 @@
/** Returns an instruction that can be used to materialize this {@link DexValue} (or null). */
public ConstInstruction asConstInstruction(
- AppView<? extends AppInfoWithSubtyping> appView, IRCode code, DebugLocalInfo local) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) {
return null;
}
@@ -413,6 +418,11 @@
public DexValueNumber asDexValueNumber() {
return this;
}
+
+ @Override
+ public AbstractValue toAbstractValue(AbstractValueFactory factory) {
+ return factory.createSingleNumberValue(getRawValue());
+ }
}
public static class DexValueByte extends DexValueNumber {
@@ -494,7 +504,7 @@
@Override
public ConstInstruction asConstInstruction(
- AppView<? extends AppInfoWithSubtyping> appView, IRCode code, DebugLocalInfo local) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) {
return code.createIntConstant(value, local);
}
}
@@ -576,7 +586,7 @@
@Override
public ConstInstruction asConstInstruction(
- AppView<? extends AppInfoWithSubtyping> appView, IRCode code, DebugLocalInfo local) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) {
return code.createIntConstant(value, local);
}
}
@@ -662,7 +672,7 @@
@Override
public ConstInstruction asConstInstruction(
- AppView<? extends AppInfoWithSubtyping> appView, IRCode code, DebugLocalInfo local) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) {
return code.createIntConstant(value, local);
}
}
@@ -744,7 +754,7 @@
@Override
public ConstInstruction asConstInstruction(
- AppView<? extends AppInfoWithSubtyping> appView, IRCode code, DebugLocalInfo local) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) {
return code.createIntConstant(value, local);
}
}
@@ -826,7 +836,7 @@
@Override
public ConstInstruction asConstInstruction(
- AppView<? extends AppInfoWithSubtyping> appView, IRCode code, DebugLocalInfo local) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) {
return code.createLongConstant(value, local);
}
}
@@ -894,7 +904,7 @@
@Override
public ConstInstruction asConstInstruction(
- AppView<? extends AppInfoWithSubtyping> appView, IRCode code, DebugLocalInfo local) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) {
return code.createFloatConstant(value, local);
}
@@ -982,7 +992,7 @@
@Override
public ConstInstruction asConstInstruction(
- AppView<? extends AppInfoWithSubtyping> appView, IRCode code, DebugLocalInfo local) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) {
return code.createDoubleConstant(value, local);
}
@@ -1110,7 +1120,7 @@
@Override
public ConstInstruction asConstInstruction(
- AppView<? extends AppInfoWithSubtyping> appView, IRCode code, DebugLocalInfo local) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) {
TypeElement type = TypeElement.stringClassType(appView, definitelyNotNull());
Value outValue = code.createValue(type, local);
ConstString instruction =
@@ -1126,6 +1136,11 @@
// Assuming that strings do not have side-effects.
return false;
}
+
+ @Override
+ public AbstractValue toAbstractValue(AbstractValueFactory factory) {
+ return factory.createSingleStringValue(value);
+ }
}
public static class DexItemBasedValueString extends NestedDexValue<DexReference> {
@@ -1168,7 +1183,7 @@
@Override
public ConstInstruction asConstInstruction(
- AppView<? extends AppInfoWithSubtyping> appView, IRCode code, DebugLocalInfo local) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) {
TypeElement type = TypeElement.stringClassType(appView, definitelyNotNull());
Value outValue = code.createValue(type, local);
DexItemBasedConstString instruction =
@@ -1183,6 +1198,13 @@
}
@Override
+ public AbstractValue toAbstractValue(AbstractValueFactory factory) {
+ // TODO(b/150835624): Update once there is an abstract value to represent dex item based
+ // strings.
+ return UnknownValue.getInstance();
+ }
+
+ @Override
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
throw new Unreachable(
"DexItemBasedValueString values should always be rewritten into DexValueString");
@@ -1215,6 +1237,11 @@
public boolean isDexValueType() {
return true;
}
+
+ @Override
+ public AbstractValue toAbstractValue(AbstractValueFactory factory) {
+ return UnknownValue.getInstance();
+ }
}
static public class DexValueField extends NestedDexValue<DexField> {
@@ -1243,6 +1270,11 @@
public DexValueField asDexValueField() {
return this;
}
+
+ @Override
+ public AbstractValue toAbstractValue(AbstractValueFactory factory) {
+ return UnknownValue.getInstance();
+ }
}
static public class DexValueMethod extends NestedDexValue<DexMethod> {
@@ -1271,6 +1303,11 @@
public DexValueMethod asDexValueMethod() {
return this;
}
+
+ @Override
+ public AbstractValue toAbstractValue(AbstractValueFactory factory) {
+ return UnknownValue.getInstance();
+ }
}
static public class DexValueEnum extends NestedDexValue<DexField> {
@@ -1299,6 +1336,11 @@
public DexValueEnum asDexValueEnum() {
return this;
}
+
+ @Override
+ public AbstractValue toAbstractValue(AbstractValueFactory factory) {
+ return UnknownValue.getInstance();
+ }
}
static public class DexValueMethodType extends NestedDexValue<DexProto> {
@@ -1327,6 +1369,11 @@
IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
value.collectIndexedItems(indexedItems, method, instructionOffset);
}
+
+ @Override
+ public AbstractValue toAbstractValue(AbstractValueFactory factory) {
+ return UnknownValue.getInstance();
+ }
}
static public class DexValueArray extends DexValue {
@@ -1372,6 +1419,11 @@
}
@Override
+ public AbstractValue toAbstractValue(AbstractValueFactory factory) {
+ return UnknownValue.getInstance();
+ }
+
+ @Override
public Object asAsmEncodedObject() {
throw new Unreachable("No ASM conversion for DexValueArray");
}
@@ -1446,6 +1498,11 @@
}
@Override
+ public AbstractValue toAbstractValue(AbstractValueFactory factory) {
+ return UnknownValue.getInstance();
+ }
+
+ @Override
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
writeHeader(DexValueKind.ANNOTATION, 0, dest);
FileWriter.writeEncodedAnnotation(value, dest, mapping);
@@ -1566,7 +1623,7 @@
@Override
public ConstInstruction asConstInstruction(
- AppView<? extends AppInfoWithSubtyping> appView, IRCode code, DebugLocalInfo local) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) {
return code.createConstNull(local);
}
}
@@ -1652,7 +1709,7 @@
@Override
public ConstInstruction asConstInstruction(
- AppView<? extends AppInfoWithSubtyping> appView, IRCode code, DebugLocalInfo local) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) {
return code.createIntConstant(BooleanUtils.intValue(value), local);
}
}
@@ -1683,5 +1740,10 @@
IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
value.collectIndexedItems(indexedItems, method, instructionOffset);
}
+
+ @Override
+ public AbstractValue toAbstractValue(AbstractValueFactory factory) {
+ return UnknownValue.getInstance();
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/LookupCompletenessHelper.java b/src/main/java/com/android/tools/r8/graph/LookupCompletenessHelper.java
index 64fa1a6..5a8f39f 100644
--- a/src/main/java/com/android/tools/r8/graph/LookupCompletenessHelper.java
+++ b/src/main/java/com/android/tools/r8/graph/LookupCompletenessHelper.java
@@ -40,7 +40,7 @@
void checkDexClassAndMethod(DexClassAndMethod classAndMethod) {
checkClass(classAndMethod.getHolder());
- checkMethod(classAndMethod.getMethod());
+ checkMethod(classAndMethod.getDefinition());
}
LookupResultCollectionState computeCollectionState(
diff --git a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
index fbecfef..fd1781d 100644
--- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
@@ -3,12 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.LookupResult.LookupResultSuccess.LookupResultCollectionState;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.InstantiatedObject;
import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.OptionalBool;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -59,10 +59,10 @@
return isSingleResolution() ? asSingleResolution().getResolvedMethod() : null;
}
- public abstract boolean isAccessibleFrom(
+ public abstract OptionalBool isAccessibleFrom(
DexProgramClass context, AppInfoWithClassHierarchy appInfo);
- public abstract boolean isAccessibleForVirtualDispatchFrom(
+ public abstract OptionalBool isAccessibleForVirtualDispatchFrom(
DexProgramClass context, AppInfoWithClassHierarchy appInfo);
public abstract boolean isVirtualTarget();
@@ -154,15 +154,19 @@
}
@Override
- public boolean isAccessibleFrom(DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+ public OptionalBool isAccessibleFrom(
+ DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
return AccessControl.isMethodAccessible(
resolvedMethod, initialResolutionHolder, context, appInfo);
}
@Override
- public boolean isAccessibleForVirtualDispatchFrom(
+ public OptionalBool isAccessibleForVirtualDispatchFrom(
DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
- return resolvedMethod.isVirtualMethod() && isAccessibleFrom(context, appInfo);
+ if (resolvedMethod.isVirtualMethod()) {
+ return isAccessibleFrom(context, appInfo);
+ }
+ return OptionalBool.FALSE;
}
@Override
@@ -180,7 +184,7 @@
public DexEncodedMethod lookupInvokeSpecialTarget(
DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
// If the resolution is non-accessible then no target exists.
- if (isAccessibleFrom(context, appInfo)) {
+ if (isAccessibleFrom(context, appInfo).isPossiblyTrue()) {
return internalInvokeSpecialOrSuper(
context, appInfo, (sup, sub) -> isSuperclass(sup, sub, appInfo));
}
@@ -205,16 +209,13 @@
@Override
public DexEncodedMethod lookupInvokeSuperTarget(
DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
- // TODO(b/147848950): Investigate and remove the Compilation error. It could compile to
- // throw IAE.
if (resolvedMethod.isInstanceInitializer()
- || (appInfo.hasSubtyping()
- && initialResolutionHolder != context
- && !isSuperclass(initialResolutionHolder, context, appInfo.withSubtyping()))) {
- throw new CompilationError(
- "Illegal invoke-super to " + resolvedMethod.toSourceString(), context.getOrigin());
+ || (initialResolutionHolder != context
+ && !isSuperclass(initialResolutionHolder, context, appInfo))) {
+ // If the target is <init> or not on a super class then the call is invalid.
+ return null;
}
- if (isAccessibleFrom(context, appInfo)) {
+ if (isAccessibleFrom(context, appInfo).isPossiblyTrue()) {
return internalInvokeSpecialOrSuper(context, appInfo, (sup, sub) -> true);
}
return null;
@@ -233,7 +234,7 @@
@Override
public DexEncodedMethod lookupInvokeStaticTarget(DexProgramClass context,
AppInfoWithClassHierarchy appInfo) {
- if (!isAccessibleFrom(context, appInfo)) {
+ if (isAccessibleFrom(context, appInfo).isFalse()) {
return null;
}
if (resolvedMethod.isStatic()) {
@@ -254,7 +255,7 @@
@Override
public DexEncodedMethod lookupInvokeDirectTarget(
DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
- if (!isAccessibleFrom(context, appInfo)) {
+ if (isAccessibleFrom(context, appInfo).isFalse()) {
return null;
}
if (resolvedMethod.isDirectMethod()) {
@@ -313,7 +314,7 @@
if (target == null) {
DexClassAndMethod result = appInfo.lookupMaximallySpecificMethod(initialType, method);
if (result != null) {
- target = result.getMethod();
+ target = result.getDefinition();
}
}
if (target == null) {
@@ -341,7 +342,7 @@
private static boolean isSuperclass(
DexClass sup, DexClass sub, AppInfoWithClassHierarchy appInfo) {
- return sup != sub && appInfo.isSubtype(sub.type, sup.type);
+ return appInfo.isStrictSubtypeOf(sub.type, sup.type);
}
@Override
@@ -353,7 +354,7 @@
// Check that the initial resolution holder is accessible from the context.
assert appInfo.isSubtype(initialResolutionHolder.type, resolvedHolder.type)
: initialResolutionHolder.type + " is not a subtype of " + resolvedHolder.type;
- if (context != null && !isAccessibleFrom(context, appInfo)) {
+ if (context != null && isAccessibleFrom(context, appInfo).isFalse()) {
return LookupResult.createFailedResult();
}
if (resolvedMethod.isPrivateMethod()) {
@@ -466,7 +467,7 @@
DexClassAndMethod target,
boolean holderIsInterface,
Map<DexEncodedMethod, DexClassAndMethod> result) {
- DexEncodedMethod targetMethod = target.getMethod();
+ DexEncodedMethod targetMethod = target.getDefinition();
assert !targetMethod.isPrivateMethod();
if (holderIsInterface) {
// Add default interface methods to the list of targets.
@@ -726,14 +727,15 @@
}
@Override
- public boolean isAccessibleFrom(DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
- return true;
+ public OptionalBool isAccessibleFrom(
+ DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+ return OptionalBool.TRUE;
}
@Override
- public boolean isAccessibleForVirtualDispatchFrom(
+ public OptionalBool isAccessibleForVirtualDispatchFrom(
DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
- return true;
+ return OptionalBool.TRUE;
}
@Override
@@ -760,14 +762,15 @@
}
@Override
- public boolean isAccessibleFrom(DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
- return false;
+ public OptionalBool isAccessibleFrom(
+ DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+ return OptionalBool.FALSE;
}
@Override
- public boolean isAccessibleForVirtualDispatchFrom(
+ public OptionalBool isAccessibleForVirtualDispatchFrom(
DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
- return false;
+ return OptionalBool.FALSE;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java b/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
index 6c6e3ba..662ccb0 100644
--- a/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
@@ -14,6 +14,7 @@
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;
@@ -46,6 +47,18 @@
populateSubtypeMap(classes, definitions::definitionFor, factory);
}
+ public boolean verifyEquals(Collection<DexClass> classes, DexDefinitionSupplier definitions) {
+ SubtypingInfo subtypingInfo = new SubtypingInfo(classes, definitions);
+ assert typeInfo.equals(subtypingInfo.typeInfo);
+ assert subtypeMap.keySet().equals(subtypingInfo.subtypeMap.keySet());
+ subtypeMap.forEach(
+ (key, value) -> {
+ assert subtypingInfo.subtypeMap.get(key).equals(value);
+ });
+ assert missingClasses.equals(subtypingInfo.missingClasses);
+ return true;
+ }
+
private void populateSuperType(
Map<DexType, Set<DexType>> map,
DexType superType,
@@ -239,6 +252,20 @@
}
@Override
+ public int hashCode() {
+ return Objects.hash(type, directSubtypes);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof TypeInfo)) {
+ return false;
+ }
+ TypeInfo other = (TypeInfo) obj;
+ return other.type == type && other.directSubtypes.equals(directSubtypes);
+ }
+
+ @Override
public String toString() {
return "TypeInfo{" + type + ", level:" + hierarchyLevel + "}";
}
diff --git a/src/main/java/com/android/tools/r8/graph/TopDownClassHierarchyTraversal.java b/src/main/java/com/android/tools/r8/graph/TopDownClassHierarchyTraversal.java
index 72e07e1..613db50 100644
--- a/src/main/java/com/android/tools/r8/graph/TopDownClassHierarchyTraversal.java
+++ b/src/main/java/com/android/tools/r8/graph/TopDownClassHierarchyTraversal.java
@@ -10,7 +10,7 @@
extends ClassHierarchyTraversal<T, TopDownClassHierarchyTraversal<T>> {
private TopDownClassHierarchyTraversal(
- AppView<? extends AppInfoWithSubtyping> appView, Scope scope) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, Scope scope) {
super(appView, scope);
}
@@ -19,7 +19,7 @@
* classes) that are reachable from a given set of sources.
*/
public static TopDownClassHierarchyTraversal<DexClass> forAllClasses(
- AppView<? extends AppInfoWithSubtyping> appView) {
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
return new TopDownClassHierarchyTraversal<>(appView, Scope.ALL_CLASSES);
}
@@ -28,7 +28,7 @@
* given set of sources.
*/
public static TopDownClassHierarchyTraversal<DexLibraryClass> forLibraryClasses(
- AppView<? extends AppInfoWithSubtyping> appView) {
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
return new TopDownClassHierarchyTraversal<>(appView, Scope.ONLY_LIBRARY_CLASSES);
}
@@ -37,7 +37,7 @@
* reachable from a given set of sources.
*/
public static TopDownClassHierarchyTraversal<DexClass> forLibraryAndClasspathClasses(
- AppView<? extends AppInfoWithSubtyping> appView) {
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
return new TopDownClassHierarchyTraversal<>(appView, Scope.ONLY_LIBRARY_AND_CLASSPATH_CLASSES);
}
@@ -46,7 +46,7 @@
* given set of sources.
*/
public static TopDownClassHierarchyTraversal<DexProgramClass> forProgramClasses(
- AppView<? extends AppInfoWithSubtyping> appView) {
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
return new TopDownClassHierarchyTraversal<>(appView, Scope.ONLY_PROGRAM_CLASSES);
}
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
index 7ecd527..04dbef2 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.graph.analysis;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -19,11 +19,11 @@
// A simple structure that stores the result of the analysis.
public static class InitializedClassesInInstanceMethods {
- private final AppView<? extends AppInfoWithSubtyping> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final Map<DexType, DexType> mapping;
private InitializedClassesInInstanceMethods(
- AppView<? extends AppInfoWithSubtyping> appView, Map<DexType, DexType> mapping) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, Map<DexType, DexType> mapping) {
this.appView = appView;
this.mapping = mapping;
}
@@ -31,7 +31,7 @@
public boolean isClassDefinitelyLoadedInInstanceMethodsOn(DexType subject, DexType context) {
// If `subject` is kept, then it is instantiated by reflection, which means that the analysis
// has not seen all allocation sites. In that case, we conservatively return false.
- AppInfoWithSubtyping appInfo = appView.appInfo();
+ AppInfoWithClassHierarchy appInfo = appView.appInfo();
if (appInfo.hasLiveness() && appInfo.withLiveness().isPinned(subject)) {
return false;
}
@@ -50,14 +50,14 @@
}
}
- private final AppView<? extends AppInfoWithSubtyping> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
// If the mapping contains an entry `X -> Y`, then the type Y is guaranteed to be initialized in
// all instance methods of X.
private final Map<DexType, DexType> mapping = new IdentityHashMap<>();
public InitializedClassesInInstanceMethodsAnalysis(
- AppView<? extends AppInfoWithSubtyping> appView) {
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
}
@@ -73,7 +73,7 @@
}
// Record that the enclosing class is guaranteed to be initialized at the allocation site.
- AppInfoWithSubtyping appInfo = appView.appInfo();
+ AppInfoWithClassHierarchy appInfo = appView.appInfo();
DexType guaranteedToBeInitialized = context.holder();
DexType existingGuaranteedToBeInitialized =
mapping.getOrDefault(key, guaranteedToBeInitialized);
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 94ee9cf..e825ba4 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
@@ -97,7 +97,7 @@
}
public boolean isClassDefinitelyLoadedBeforeInstruction(DexType type, Instruction instruction) {
- DexType context = code.method.holder();
+ DexType context = code.method().holder();
BasicBlock block = instruction.getBlock();
// Visit the instructions in `block` prior to `instruction`.
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java
index 9ae6c86..5452f50 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/DeterminismAnalysis.java
@@ -37,7 +37,7 @@
}
if (instr.isInvokeMethod()) {
DexEncodedMethod target =
- instr.asInvokeMethod().lookupSingleTarget(appView, code.method.holder());
+ instr.asInvokeMethod().lookupSingleTarget(appView, code.method().holder());
if (target != null && target.getOptimizationInfo().returnValueOnlyDependsOnArguments()) {
continue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
index 406c9e6..215f2a1 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
@@ -36,7 +36,7 @@
public static Set<DexType> computeInitializedClassesOnNormalExit(
AppView<AppInfoWithLiveness> appView, IRCode code) {
DominatorTree dominatorTree = new DominatorTree(code, Assumption.MAY_HAVE_UNREACHABLE_BLOCKS);
- Visitor visitor = new Visitor(appView, code.method.holder());
+ Visitor visitor = new Visitor(appView, code.method().holder());
for (BasicBlock dominator : dominatorTree.normalExitDominatorBlocks()) {
if (dominator.hasCatchHandlers()) {
// When determining which classes that are guaranteed to be initialized from a given
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/TypeChecker.java b/src/main/java/com/android/tools/r8/ir/analysis/TypeChecker.java
index f8a621d..8c8eeb9 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/TypeChecker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/TypeChecker.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.ir.analysis;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -30,9 +30,9 @@
*/
public class TypeChecker {
- private final AppView<? extends AppInfoWithSubtyping> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
- public TypeChecker(AppView<? extends AppInfoWithSubtyping> appView) {
+ public TypeChecker(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
}
@@ -43,7 +43,7 @@
return false;
}
} else if (instruction.isReturn()) {
- if (!check(instruction.asReturn(), code.method)) {
+ if (!check(instruction.asReturn(), code.method())) {
return false;
}
} else if (instruction.isStaticPut()) {
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 f417d65..fbb6735 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
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.code.ArrayPut;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -80,7 +81,7 @@
public ValueMayDependOnEnvironmentAnalysis(AppView<?> appView, IRCode code) {
this.appView = appView;
this.code = code;
- this.context = code.method.holder();
+ this.context = code.method().holder();
}
public boolean valueMayDependOnEnvironment(Value value) {
@@ -109,8 +110,13 @@
if (isConstantArrayThroughoutMethod(root, assumedNotToDependOnEnvironment)) {
return false;
}
- if (root.getAbstractValue(appView, context).isSingleEnumValue()) {
- return false;
+ AbstractValue abstractValue = root.getAbstractValue(appView, context);
+ if (abstractValue.isSingleFieldValue()) {
+ DexEncodedField field =
+ appView.definitionFor(abstractValue.asSingleFieldValue().getField());
+ if (field != null && field.isEnum()) {
+ return false;
+ }
}
if (isNewInstanceWithoutEnvironmentDependentFields(root, assumedNotToDependOnEnvironment)) {
return false;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/escape/EscapeAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/escape/EscapeAnalysis.java
index 43e484d..b1cac80 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/escape/EscapeAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/escape/EscapeAnalysis.java
@@ -137,8 +137,8 @@
}
}
}
- if (!configuration.isLegitimateEscapeRoute(appView, this, user, code.method.method)
- && isDirectlyEscaping(user, code.method.method, arguments)) {
+ if (!configuration.isLegitimateEscapeRoute(appView, this, user, code.method().method)
+ && isDirectlyEscaping(user, code.method().method, arguments)) {
if (stoppingCriterion.test(user)) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
index 1e8bb52..7c148a4 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
@@ -74,7 +74,7 @@
DexEncodedField encodedField = appView.appInfo().resolveField(fieldInstruction.getField());
if (encodedField != null && encodedField.isProgramField(appView)) {
if (fieldAssignmentTracker != null) {
- fieldAssignmentTracker.recordFieldAccess(fieldInstruction, encodedField, code.method);
+ fieldAssignmentTracker.recordFieldAccess(fieldInstruction, encodedField, code.method());
}
if (fieldBitAccessAnalysis != null) {
fieldBitAccessAnalysis.recordFieldAccess(fieldInstruction, encodedField, feedback);
@@ -85,7 +85,7 @@
DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(newInstance.clazz));
if (clazz != null) {
if (fieldAssignmentTracker != null) {
- fieldAssignmentTracker.recordAllocationSite(newInstance, clazz, code.method);
+ fieldAssignmentTracker.recordAllocationSite(newInstance, clazz, code.method());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
index e2ab677..f488267 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
-import static com.android.tools.r8.ir.code.Opcodes.*;
+import static com.android.tools.r8.ir.code.Opcodes.ARRAY_PUT;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT;
+import static com.android.tools.r8.ir.code.Opcodes.STATIC_PUT;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
@@ -17,8 +19,8 @@
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.AbstractValueFactory;
-import com.android.tools.r8.ir.analysis.value.SingleEnumValue;
-import com.android.tools.r8.ir.analysis.value.UnknownValue;
+import com.android.tools.r8.ir.analysis.value.ObjectState;
+import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
import com.android.tools.r8.ir.code.ArrayPut;
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
@@ -28,6 +30,8 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization.ClassInitializerDefaultsResult;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.ir.optimize.info.field.InstanceFieldArgumentInitializationInfo;
+import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Timing;
@@ -100,10 +104,9 @@
void updateFieldOptimizationInfo(DexEncodedField field, FieldInstruction fieldPut, Value value) {
// Abstract value.
Value root = value.getAliasedValue();
- AbstractValue abstractValue = computeAbstractValue(root);
+ AbstractValue abstractValue = root.getAbstractValue(appView, clazz.type);
if (abstractValue.isUnknown()) {
- feedback.recordFieldHasAbstractValue(
- field, appView, appView.abstractValueFactory().createSingleFieldValue(field.field));
+ feedback.recordFieldHasAbstractValue(field, appView, computeSingleFieldValue(field, root));
} else {
feedback.recordFieldHasAbstractValue(field, appView, abstractValue);
}
@@ -124,33 +127,29 @@
}
}
- private AbstractValue computeAbstractValue(Value value) {
+ private SingleFieldValue computeSingleFieldValue(DexEncodedField field, Value value) {
assert !value.hasAliasedValue();
- if (clazz.isEnum()) {
- SingleEnumValue singleEnumValue = getSingleEnumValue(value);
- if (singleEnumValue != null) {
- return singleEnumValue;
- }
+ SingleFieldValue result = computeSingleEnumFieldValue(value);
+ if (result != null) {
+ return result;
}
- if (!value.isPhi()) {
- return value.definition.getAbstractValue(appView, clazz.type);
- }
- return UnknownValue.getInstance();
+ return appView
+ .abstractValueFactory()
+ .createSingleFieldValue(field.field, computeObjectState(value));
}
/**
* If {@param value} is defined by a new-instance instruction that instantiates the enclosing enum
* class, and the value is assigned into exactly one static enum field on the enclosing enum
- * class, then returns a {@link SingleEnumValue} instance. Otherwise, returns {@code null}.
+ * class, then returns a {@link SingleFieldValue} instance. Otherwise, returns {@code null}.
*
* <p>Note that enum constructors also store the newly instantiated enums in the {@code $VALUES}
* array field on the enum. Therefore, this code also allows {@param value} to be stored into an
* array as long as the array is identified as being the {@code $VALUES} array.
*/
- private SingleEnumValue getSingleEnumValue(Value value) {
- assert clazz.isEnum();
+ private SingleFieldValue computeSingleEnumFieldValue(Value value) {
assert !value.hasAliasedValue();
- if (value.isPhi() || !value.definition.isNewInstance()) {
+ if (!clazz.isEnum() || !value.isDefinedByInstructionSatisfying(Instruction::isNewInstance)) {
return null;
}
@@ -202,7 +201,53 @@
return null;
}
- return appView.abstractValueFactory().createSingleEnumValue(enumField.field);
+ return appView
+ .abstractValueFactory()
+ .createSingleFieldValue(enumField.field, computeObjectState(value));
+ }
+
+ private ObjectState computeObjectState(Value value) {
+ assert !value.hasAliasedValue();
+ if (!value.isDefinedByInstructionSatisfying(Instruction::isNewInstance)) {
+ return ObjectState.empty();
+ }
+
+ NewInstance newInstance = value.definition.asNewInstance();
+ InvokeDirect uniqueConstructorInvoke =
+ newInstance.getUniqueConstructorInvoke(appView.dexItemFactory());
+ if (uniqueConstructorInvoke == null) {
+ return ObjectState.empty();
+ }
+
+ DexEncodedMethod singleTarget = uniqueConstructorInvoke.lookupSingleTarget(appView, clazz.type);
+ if (singleTarget == null) {
+ return ObjectState.empty();
+ }
+
+ InstanceFieldInitializationInfoCollection initializationInfos =
+ singleTarget.getOptimizationInfo().getInstanceInitializerInfo().fieldInitializationInfos();
+ if (initializationInfos.isEmpty()) {
+ return ObjectState.empty();
+ }
+
+ ObjectState.Builder builder = ObjectState.builder();
+ initializationInfos.forEach(
+ appView,
+ (field, initializationInfo) -> {
+ if (!appView.appInfo().isInstanceFieldWrittenOnlyInInstanceInitializers(field)) {
+ return;
+ }
+ if (initializationInfo.isArgumentInitializationInfo()) {
+ InstanceFieldArgumentInitializationInfo argumentInitializationInfo =
+ initializationInfo.asArgumentInitializationInfo();
+ Value argument =
+ uniqueConstructorInvoke.getArgument(argumentInitializationInfo.getArgumentIndex());
+ builder.recordFieldHasValue(field, argument.getAbstractValue(appView, clazz.type));
+ } else if (initializationInfo.isSingleValue()) {
+ builder.recordFieldHasValue(field, initializationInfo.asSingleValue());
+ }
+ });
+ return builder.build();
}
private boolean isEnumValuesArray(Value value) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
index 2f152e8..f073108 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.ir.analysis.proto;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
@@ -52,13 +51,13 @@
public class GeneratedMessageLiteBuilderShrinker {
- private final AppView<? extends AppInfoWithSubtyping> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final ProtoReferences references;
private final Map<DexProgramClass, DexEncodedMethod> builders = new IdentityHashMap<>();
GeneratedMessageLiteBuilderShrinker(
- AppView<? extends AppInfoWithSubtyping> appView, ProtoReferences references) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, ProtoReferences references) {
this.appView = appView;
this.references = references;
}
@@ -117,7 +116,7 @@
}
public static void addInliningHeuristicsForBuilderInlining(
- AppView<? extends AppInfoWithSubtyping> appView,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
SubtypingInfo subtypingInfo,
PredicateSet<DexType> alwaysClassInline,
Set<DexType> neverMerge,
@@ -279,7 +278,7 @@
private static class RootSetExtension {
- private final AppView<? extends AppInfoWithSubtyping> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final ProtoReferences references;
private final PredicateSet<DexType> alwaysClassInline;
@@ -289,7 +288,7 @@
private final Set<DexMethod> bypassClinitforInlining;
RootSetExtension(
- AppView<? extends AppInfoWithSubtyping> appView,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
PredicateSet<DexType> alwaysClassInline,
Set<DexType> neverMerge,
Set<DexMethod> alwaysInline,
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java
index 0d500e9..b3aaf9c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/sideeffect/ClassInitializerSideEffectAnalysis.java
@@ -39,7 +39,7 @@
*/
public static ClassInitializerSideEffect classInitializerCanBePostponed(
AppView<?> appView, IRCode code) {
- DexType context = code.method.holder();
+ DexType context = code.method().holder();
OptionalBool controlFlowMayDependOnEnvironment = OptionalBool.unknown();
boolean mayHaveSideEffects = false;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java
index 4af45c6..4821b2b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java
@@ -5,7 +5,7 @@
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
@@ -88,7 +88,7 @@
}
@Override
- public boolean isBasedOnMissingClass(AppView<? extends AppInfoWithSubtyping> appView) {
+ public boolean isBasedOnMissingClass(AppView<? extends AppInfoWithClassHierarchy> appView) {
return memberTypeLattice.isBasedOnMissingClass(appView);
}
@@ -129,7 +129,7 @@
@Override
public ArrayTypeElement fixupClassTypeReferences(
- Function<DexType, DexType> mapping, AppView<? extends AppInfoWithSubtyping> appView) {
+ Function<DexType, DexType> mapping, AppView<? extends AppInfoWithClassHierarchy> appView) {
if (memberTypeLattice.isReferenceType()) {
TypeElement substitutedMemberType =
memberTypeLattice.fixupClassTypeReferences(mapping, appView);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
index 8a47047..e71ef7d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.ir.analysis.type;
import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexType;
@@ -28,7 +28,7 @@
// Least upper bound of interfaces that this class type is implementing.
// Lazily computed on demand via DexItemFactory, where the canonicalized set will be maintained.
private Set<DexType> lazyInterfaces;
- private AppView<? extends AppInfoWithSubtyping> appView;
+ private AppView<? extends AppInfoWithClassHierarchy> appView;
// On-demand link between other nullability-variants.
private final NullabilityVariants<ClassTypeElement> variants;
private final DexType type;
@@ -42,7 +42,9 @@
}
public static ClassTypeElement create(
- DexType classType, Nullability nullability, AppView<? extends AppInfoWithSubtyping> appView) {
+ DexType classType,
+ Nullability nullability,
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
assert appView != null;
return NullabilityVariants.create(
nullability,
@@ -54,7 +56,7 @@
Nullability nullability,
Set<DexType> interfaces,
NullabilityVariants<ClassTypeElement> variants,
- AppView<? extends AppInfoWithSubtyping> appView) {
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
super(nullability);
assert classType.isClassType();
assert interfaces != null || appView != null;
@@ -99,9 +101,8 @@
return variants.getOrCreateElement(nullability, this::createVariant);
}
-
@Override
- public boolean isBasedOnMissingClass(AppView<? extends AppInfoWithSubtyping> appView) {
+ public boolean isBasedOnMissingClass(AppView<? extends AppInfoWithClassHierarchy> appView) {
return appView.appInfo().isMissingOrHasMissingSuperType(getClassType())
|| getInterfaces().stream()
.anyMatch(type -> appView.appInfo().isMissingOrHasMissingSuperType(type));
@@ -145,7 +146,7 @@
@Override
public TypeElement fixupClassTypeReferences(
- Function<DexType, DexType> mapping, AppView<? extends AppInfoWithSubtyping> appView) {
+ Function<DexType, DexType> mapping, AppView<? extends AppInfoWithClassHierarchy> appView) {
DexType mappedType = mapping.apply(type);
if (mappedType.isPrimitiveType()) {
return PrimitiveTypeElement.fromDexType(mappedType, false);
@@ -197,7 +198,7 @@
ClassTypeElement join(ClassTypeElement other, AppView<?> appView) {
Nullability nullability = nullability().join(other.nullability());
- if (!appView.appInfo().hasSubtyping()) {
+ if (!appView.enableWholeProgramOptimizations()) {
assert lazyInterfaces != null && lazyInterfaces.isEmpty();
assert other.lazyInterfaces != null && other.lazyInterfaces.isEmpty();
return ClassTypeElement.create(
@@ -209,7 +210,7 @@
}
DexType lubType =
computeLeastUpperBoundOfClasses(
- appView.appInfo().withSubtyping(), getClassType(), other.getClassType());
+ appView.appInfo().withClassHierarchy(), getClassType(), other.getClassType());
Set<DexType> c1lubItfs = getInterfaces();
Set<DexType> c2lubItfs = other.getInterfaces();
Set<DexType> lubItfs = null;
@@ -217,7 +218,8 @@
lubItfs = c1lubItfs;
}
if (lubItfs == null) {
- lubItfs = computeLeastUpperBoundOfInterfaces(appView.withSubtyping(), c1lubItfs, c2lubItfs);
+ lubItfs =
+ computeLeastUpperBoundOfInterfaces(appView.withClassHierarchy(), c1lubItfs, c2lubItfs);
}
return ClassTypeElement.create(lubType, nullability, lubItfs);
}
@@ -238,7 +240,7 @@
}
public static DexType computeLeastUpperBoundOfClasses(
- AppInfoWithSubtyping appInfo, DexType type1, DexType type2) {
+ AppInfoWithClassHierarchy appInfo, DexType type1, DexType type2) {
// Compiling R8 with R8, this hits more than 1/3 of cases.
if (type1 == type2) {
return type1;
@@ -282,7 +284,7 @@
}
public static Set<DexType> computeLeastUpperBoundOfInterfaces(
- AppView<? extends AppInfoWithSubtyping> appView, Set<DexType> s1, Set<DexType> s2) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, Set<DexType> s1, Set<DexType> s2) {
if (s1.isEmpty() || s2.isEmpty()) {
return Collections.emptySet();
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/DestructivePhiTypeUpdater.java b/src/main/java/com/android/tools/r8/ir/analysis/type/DestructivePhiTypeUpdater.java
index 6c1136f..29ca247 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/DestructivePhiTypeUpdater.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/DestructivePhiTypeUpdater.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.ir.analysis.type;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.BasicBlock;
@@ -20,15 +20,15 @@
public class DestructivePhiTypeUpdater {
- private final AppView<? extends AppInfoWithSubtyping> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final Function<DexType, DexType> mapping;
- public DestructivePhiTypeUpdater(AppView<? extends AppInfoWithSubtyping> appView) {
+ public DestructivePhiTypeUpdater(AppView<? extends AppInfoWithClassHierarchy> appView) {
this(appView, appView.graphLense()::lookupType);
}
public DestructivePhiTypeUpdater(
- AppView<? extends AppInfoWithSubtyping> appView, Function<DexType, DexType> mapping) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, Function<DexType, DexType> mapping) {
this.appView = appView;
this.mapping = mapping;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
index ffd23cc..0e3f404 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
@@ -3,8 +3,7 @@
// 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.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
@@ -157,12 +156,12 @@
}
public static DexType getRefinedReceiverType(
- AppView<? extends AppInfoWithSubtyping> appView, InvokeMethodWithReceiver invoke) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, InvokeMethodWithReceiver invoke) {
return getRefinedReceiverType(appView, invoke.getInvokedMethod(), invoke.getReceiver());
}
public static DexType getRefinedReceiverType(
- AppView<? extends AppInfoWithSubtyping> appView, DexMethod method, Value receiver) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, DexMethod method, Value receiver) {
TypeElement lattice = receiver.getDynamicUpperBoundType(appView);
DexType staticReceiverType = method.holder;
if (lattice.isClassType()) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
index ecc7340..10f5ece 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.ir.analysis.type;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexItemFactory;
@@ -68,7 +68,7 @@
}
public TypeElement fixupClassTypeReferences(
- Function<DexType, DexType> mapping, AppView<? extends AppInfoWithSubtyping> appView) {
+ Function<DexType, DexType> mapping, AppView<? extends AppInfoWithClassHierarchy> appView) {
return this;
}
@@ -228,7 +228,7 @@
* @return {@code} true if this type is based on a missing class.
* @param appView
*/
- public boolean isBasedOnMissingClass(AppView<? extends AppInfoWithSubtyping> appView) {
+ public boolean isBasedOnMissingClass(AppView<? extends AppInfoWithClassHierarchy> appView) {
return false;
}
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 404faff..9939980 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
@@ -44,14 +44,6 @@
return null;
}
- public boolean isSingleEnumValue() {
- return false;
- }
-
- public SingleEnumValue asSingleEnumValue() {
- return null;
- }
-
public boolean isSingleFieldValue() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java
index 80ca063..6583c9b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java
@@ -13,9 +13,6 @@
private ConcurrentHashMap<DexType, SingleConstClassValue> singleConstClassValues =
new ConcurrentHashMap<>();
- private ConcurrentHashMap<DexField, SingleEnumValue> singleEnumValues = new ConcurrentHashMap<>();
- private ConcurrentHashMap<DexField, SingleFieldValue> singleFieldValues =
- new ConcurrentHashMap<>();
private ConcurrentHashMap<Long, SingleNumberValue> singleNumberValues = new ConcurrentHashMap<>();
private ConcurrentHashMap<DexString, SingleStringValue> singleStringValues =
new ConcurrentHashMap<>();
@@ -24,12 +21,10 @@
return singleConstClassValues.computeIfAbsent(type, SingleConstClassValue::new);
}
- public SingleEnumValue createSingleEnumValue(DexField field) {
- return singleEnumValues.computeIfAbsent(field, SingleEnumValue::new);
- }
-
- public SingleFieldValue createSingleFieldValue(DexField field) {
- return singleFieldValues.computeIfAbsent(field, SingleFieldValue::new);
+ public SingleFieldValue createSingleFieldValue(DexField field, ObjectState state) {
+ return state.isEmpty()
+ ? new SingleStatelessFieldValue(field)
+ : new SingleStatefulFieldValue(field, state);
}
public SingleNumberValue createSingleNumberValue(long value) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/EmptyObjectState.java b/src/main/java/com/android/tools/r8/ir/analysis/value/EmptyObjectState.java
new file mode 100644
index 0000000..48b1d9c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/EmptyObjectState.java
@@ -0,0 +1,46 @@
+// 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.analysis.value;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+
+public class EmptyObjectState extends ObjectState {
+
+ private static final EmptyObjectState INSTANCE = new EmptyObjectState();
+
+ private EmptyObjectState() {}
+
+ public static EmptyObjectState getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public AbstractValue getAbstractFieldValue(DexEncodedField field) {
+ return UnknownValue.getInstance();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return true;
+ }
+
+ @Override
+ public ObjectState rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLense lens) {
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return this == o;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(this);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/NonEmptyObjectState.java b/src/main/java/com/android/tools/r8/ir/analysis/value/NonEmptyObjectState.java
new file mode 100644
index 0000000..0732b83
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/NonEmptyObjectState.java
@@ -0,0 +1,68 @@
+// 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.analysis.value;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+public class NonEmptyObjectState extends ObjectState {
+
+ private final Map<DexField, AbstractValue> state;
+
+ /** Intentionally package private, use {@link ObjectState.Builder}. */
+ NonEmptyObjectState(Map<DexField, AbstractValue> state) {
+ assert !state.isEmpty();
+ assert state.values().stream().noneMatch(AbstractValue::isUnknown);
+ this.state = state;
+ }
+
+ @Override
+ public AbstractValue getAbstractFieldValue(DexEncodedField field) {
+ return state.getOrDefault(field.field, UnknownValue.getInstance());
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ @Override
+ public ObjectState rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLense lens) {
+ Map<DexField, AbstractValue> rewrittenState = new IdentityHashMap<>();
+ state.forEach(
+ (field, value) ->
+ rewrittenState.put(lens.lookupField(field), value.rewrittenWithLens(appView, lens)));
+ return new NonEmptyObjectState(rewrittenState);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (getClass() != o.getClass()) {
+ return false;
+ }
+ NonEmptyObjectState other = (NonEmptyObjectState) o;
+ if (state.size() != other.state.size()) {
+ return false;
+ }
+ for (DexField dexField : state.keySet()) {
+ AbstractValue localValue = state.get(dexField);
+ AbstractValue otherValue = other.state.get(dexField);
+ if (!localValue.equals(otherValue)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return state.hashCode();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/ObjectState.java b/src/main/java/com/android/tools/r8/ir/analysis/value/ObjectState.java
new file mode 100644
index 0000000..066b305
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/ObjectState.java
@@ -0,0 +1,53 @@
+// 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.analysis.value;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+public abstract class ObjectState {
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static ObjectState empty() {
+ return EmptyObjectState.getInstance();
+ }
+
+ public abstract AbstractValue getAbstractFieldValue(DexEncodedField field);
+
+ public abstract boolean isEmpty();
+
+ public abstract ObjectState rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLense lens);
+
+ @Override
+ public abstract boolean equals(Object o);
+
+ @Override
+ public abstract int hashCode();
+
+ public static class Builder {
+
+ private final Map<DexField, AbstractValue> state = new IdentityHashMap<>();
+
+ public void recordFieldHasValue(DexEncodedField field, AbstractValue abstractValue) {
+ if (!abstractValue.isUnknown()) {
+ assert !state.containsKey(field.field);
+ state.put(field.field, abstractValue);
+ }
+ }
+
+ public ObjectState build() {
+ return state.isEmpty() ? empty() : new NonEmptyObjectState(state);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
index 3f5640d..b45ec56 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleConstClassValue.java
@@ -6,9 +6,9 @@
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import static com.android.tools.r8.ir.analysis.type.TypeElement.classClassType;
-import static com.android.tools.r8.optimize.MemberRebindingAnalysis.isClassTypeVisibleFromContext;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AccessControl;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexClass;
@@ -62,7 +62,9 @@
@Override
public Instruction createMaterializingInstruction(
- AppView<? extends AppInfoWithSubtyping> appView, IRCode code, TypeAndLocalInfoSupplier info) {
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ IRCode code,
+ TypeAndLocalInfoSupplier info) {
TypeElement typeLattice = info.getOutType();
DebugLocalInfo debugLocalInfo = info.getLocalInfo();
assert typeLattice.isClassType();
@@ -72,18 +74,22 @@
Value returnedValue =
code.createValue(classClassType(appView, definitelyNotNull()), debugLocalInfo);
ConstClass instruction = new ConstClass(returnedValue, type);
- assert !instruction.instructionMayHaveSideEffects(appView, code.method.holder());
+ assert !instruction.instructionMayHaveSideEffects(appView, code.method().holder());
return instruction;
}
@Override
- public boolean isMaterializableInContext(AppView<?> appView, DexType context) {
+ public boolean isMaterializableInContext(AppView<AppInfoWithLiveness> appView, DexType context) {
DexType baseType = type.toBaseType(appView.dexItemFactory());
if (baseType.isClassType()) {
DexClass clazz = appView.definitionFor(type);
return clazz != null
&& clazz.isResolvable(appView)
- && isClassTypeVisibleFromContext(appView, context, clazz);
+ && AccessControl.isClassAccessible(
+ clazz,
+ appView.definitionFor(context).asProgramClass(),
+ appView.options().featureSplitConfiguration)
+ .isTrue();
}
assert baseType.isPrimitiveType();
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleEnumValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleEnumValue.java
deleted file mode 100644
index 4beb525..0000000
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleEnumValue.java
+++ /dev/null
@@ -1,48 +0,0 @@
-// 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.analysis.value;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
-import com.android.tools.r8.graph.GraphLense;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-
-public class SingleEnumValue extends SingleFieldValue {
-
- /** Intentionally package private, use {@link AbstractValueFactory} instead. */
- SingleEnumValue(DexField field) {
- super(field);
- }
-
- @Override
- public boolean isSingleEnumValue() {
- return true;
- }
-
- @Override
- public SingleEnumValue asSingleEnumValue() {
- return this;
- }
-
- @Override
- public String toString() {
- return "SingleEnumValue(" + getField().toSourceString() + ")";
- }
-
- @Override
- public SingleValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLense lens) {
- DexField field = getField();
- EnumValueInfoMap unboxedEnumInfo = appView.unboxedEnums().getEnumValueInfoMap(field.type);
- if (unboxedEnumInfo != null) {
- // Return the ordinal of the unboxed enum.
- assert unboxedEnumInfo.hasEnumValueInfo(field);
- return appView
- .abstractValueFactory()
- .createSingleNumberValue(unboxedEnumInfo.getEnumValueInfo(field).convertToInt());
- }
- return appView.abstractValueFactory().createSingleEnumValue(lens.lookupField(field));
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
index 855694a..46261f5 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
@@ -5,14 +5,15 @@
package com.android.tools.r8.ir.analysis.value;
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
-import static com.android.tools.r8.optimize.MemberRebindingAnalysis.isMemberVisibleFromOriginalContext;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AccessControl;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -23,11 +24,10 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-public class SingleFieldValue extends SingleValue {
+public abstract class SingleFieldValue extends SingleValue {
- private final DexField field;
+ final DexField field;
- /** Intentionally package private, use {@link AbstractValueFactory} instead. */
SingleFieldValue(DexField field) {
this.field = field;
}
@@ -36,6 +36,8 @@
return field;
}
+ public abstract ObjectState getState();
+
public boolean mayHaveFinalizeMethodDirectlyOrIndirectly(AppView<AppInfoWithLiveness> appView) {
DexType fieldType = field.type;
if (fieldType.isClassType()) {
@@ -58,23 +60,16 @@
}
@Override
- public boolean equals(Object o) {
- return this == o;
- }
+ public abstract boolean equals(Object o);
@Override
- public int hashCode() {
- return System.identityHashCode(this);
- }
-
- @Override
- public String toString() {
- return "SingleFieldValue(" + field.toSourceString() + ")";
- }
+ public abstract int hashCode();
@Override
public Instruction createMaterializingInstruction(
- AppView<? extends AppInfoWithSubtyping> appView, IRCode code, TypeAndLocalInfoSupplier info) {
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ IRCode code,
+ TypeAndLocalInfoSupplier info) {
TypeElement type = TypeElement.fromDexType(field.type, maybeNull(), appView);
assert type.lessThanOrEqual(info.getOutType(), appView);
Value outValue = code.createValue(type, info.getLocalInfo());
@@ -82,10 +77,13 @@
}
@Override
- public boolean isMaterializableInContext(AppView<?> appView, DexType context) {
- DexEncodedField encodedField = appView.appInfo().resolveField(field);
- return isMemberVisibleFromOriginalContext(
- appView, context, encodedField.holder(), encodedField.accessFlags);
+ public boolean isMaterializableInContext(AppView<AppInfoWithLiveness> appView, DexType context) {
+ return AccessControl.isFieldAccessible(
+ appView.appInfo().resolveField(field),
+ appView.definitionForHolder(field),
+ appView.definitionFor(context).asProgramClass(),
+ appView.appInfo())
+ .isTrue();
}
@Override
@@ -108,9 +106,15 @@
@Override
public SingleValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLense lens) {
- DexField rewrittenField = lens.lookupField(field);
- assert !appView.unboxedEnums().containsEnum(field.holder)
- || !appView.appInfo().resolveField(rewrittenField).accessFlags.isEnum();
- return appView.abstractValueFactory().createSingleFieldValue(rewrittenField);
+ AbstractValueFactory factory = appView.abstractValueFactory();
+ EnumValueInfoMap unboxedEnumInfo = appView.unboxedEnums().getEnumValueInfoMap(field.type);
+ if (unboxedEnumInfo != null) {
+ // Return the ordinal of the unboxed enum.
+ assert unboxedEnumInfo.hasEnumValueInfo(field);
+ return factory.createSingleNumberValue(
+ unboxedEnumInfo.getEnumValueInfo(field).convertToInt());
+ }
+ return factory.createSingleFieldValue(
+ lens.lookupField(field), getState().rewrittenWithLens(appView, lens));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
index e2f12fe..1982181 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleNumberValue.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.ir.analysis.value;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexType;
@@ -67,7 +67,9 @@
@Override
public Instruction createMaterializingInstruction(
- AppView<? extends AppInfoWithSubtyping> appView, IRCode code, TypeAndLocalInfoSupplier info) {
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ IRCode code,
+ TypeAndLocalInfoSupplier info) {
TypeElement typeLattice = info.getOutType();
DebugLocalInfo debugLocalInfo = info.getLocalInfo();
assert !typeLattice.isReferenceType() || value == 0;
@@ -78,7 +80,7 @@
}
@Override
- public boolean isMaterializableInContext(AppView<?> appView, DexType context) {
+ public boolean isMaterializableInContext(AppView<AppInfoWithLiveness> appView, DexType context) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStatefulFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStatefulFieldValue.java
new file mode 100644
index 0000000..e9b7fde
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStatefulFieldValue.java
@@ -0,0 +1,44 @@
+// 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.analysis.value;
+
+import com.android.tools.r8.graph.DexField;
+import java.util.Objects;
+
+public class SingleStatefulFieldValue extends SingleFieldValue {
+
+ private final ObjectState state;
+
+ /** Intentionally package private, use {@link AbstractValueFactory} instead. */
+ SingleStatefulFieldValue(DexField field, ObjectState state) {
+ super(field);
+ assert !state.isEmpty();
+ this.state = state;
+ }
+
+ @Override
+ public ObjectState getState() {
+ return state;
+ }
+
+ @Override
+ public String toString() {
+ return "SingleStatefulFieldValue(" + field.toSourceString() + ")";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (getClass() != o.getClass()) {
+ return false;
+ }
+ SingleStatefulFieldValue singleFieldValue = (SingleStatefulFieldValue) o;
+ return field == singleFieldValue.field && state.equals(singleFieldValue.state);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(field, state);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStatelessFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStatelessFieldValue.java
new file mode 100644
index 0000000..80de8a3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStatelessFieldValue.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.value;
+
+import com.android.tools.r8.graph.DexField;
+
+public class SingleStatelessFieldValue extends SingleFieldValue {
+
+ /** Intentionally package private, use {@link AbstractValueFactory} instead. */
+ SingleStatelessFieldValue(DexField field) {
+ super(field);
+ }
+
+ @Override
+ public ObjectState getState() {
+ return ObjectState.empty();
+ }
+
+ @Override
+ public String toString() {
+ return "SingleStatelessFieldValue(" + field.toSourceString() + ")";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (getClass() != o.getClass()) {
+ return false;
+ }
+ SingleStatelessFieldValue singleFieldValue = (SingleStatelessFieldValue) o;
+ return field == singleFieldValue.field;
+ }
+
+ @Override
+ public int hashCode() {
+ return field.hashCode();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
index 1280264..01f80cc 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleStringValue.java
@@ -7,7 +7,7 @@
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import static com.android.tools.r8.ir.analysis.type.TypeElement.stringClassType;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexString;
@@ -62,7 +62,7 @@
@Override
public Instruction createMaterializingInstruction(
- AppView<? extends AppInfoWithSubtyping> appView,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
IRCode code,
TypeAndLocalInfoSupplier info) {
TypeElement typeLattice = info.getOutType();
@@ -81,7 +81,7 @@
}
@Override
- public boolean isMaterializableInContext(AppView<?> appView, DexType context) {
+ public boolean isMaterializableInContext(AppView<AppInfoWithLiveness> appView, DexType context) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java
index 1b3e59a..61426a0 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleValue.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.ir.analysis.value;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
@@ -36,9 +36,12 @@
* #isMaterializableInContext}.
*/
public abstract Instruction createMaterializingInstruction(
- AppView<? extends AppInfoWithSubtyping> appView, IRCode code, TypeAndLocalInfoSupplier info);
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ IRCode code,
+ TypeAndLocalInfoSupplier info);
- public abstract boolean isMaterializableInContext(AppView<?> appView, DexType context);
+ public abstract boolean isMaterializableInContext(
+ AppView<AppInfoWithLiveness> appView, DexType context);
public abstract boolean isMaterializableInAllContexts(AppView<?> appView);
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
index 0994dc0..a1ed1d7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
@@ -90,7 +90,7 @@
@Override
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
- return !instructionMayHaveSideEffects(appView, code.method.holder());
+ return !instructionMayHaveSideEffects(appView, code.method().holder());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
index cd29ec9..37daada 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
@@ -192,7 +192,7 @@
@Override
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
- return !instructionMayHaveSideEffects(appView, code.method.holder());
+ return !instructionMayHaveSideEffects(appView, code.method().holder());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Assume.java b/src/main/java/com/android/tools/r8/ir/code/Assume.java
index 9fb9f27..e5316e4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Assume.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Assume.java
@@ -177,12 +177,12 @@
if (assumption.isAssumeDynamicType()) {
outType = asAssumeDynamicType().assumption.getDynamicUpperBoundType();
}
- if (appView.appInfo().hasSubtyping()) {
+ if (appView.appInfo().hasLiveness()) {
if (outType.isClassType()
&& root.getType().isClassType()
&& appView
.appInfo()
- .withSubtyping()
+ .withLiveness()
.inDifferentHierarchy(
outType.asClassType().getClassType(),
root.getType().asClassType().getClassType())) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index 4bd58c4..26b0acd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -1886,7 +1886,6 @@
for (int i = 0; i < prevCatchTargets.size(); i++) {
int prevCatchTarget = prevCatchTargets.get(i);
DexType prevCatchGuard = prevCatchGuards.get(i);
- // TODO(sgjesse): Check sub-types of guards. Will require AppInfoWithSubtyping.
if (newCatchGuards.contains(prevCatchGuard)) {
continue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index 32c5602..b4ceb0d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -7,7 +7,7 @@
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
import static com.android.tools.r8.ir.code.DominatorTree.Assumption.MAY_HAVE_UNREACHABLE_BLOCKS;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexString;
@@ -206,17 +206,9 @@
}
@Override
- public Value insertConstNullInstruction(IRCode code, InternalOptions options) {
- ConstNumber constNumberInstruction = code.createConstNull();
- // Note that we only keep position info for throwing instructions in release mode.
- constNumberInstruction.setPosition(options.debug ? current.getPosition() : Position.none());
- add(constNumberInstruction);
- return constNumberInstruction.outValue();
- }
-
- @Override
- public Value insertConstIntInstruction(IRCode code, InternalOptions options, int value) {
- ConstNumber constNumberInstruction = code.createIntConstant(value);
+ public Value insertConstNumberInstruction(
+ IRCode code, InternalOptions options, long value, TypeElement type) {
+ ConstNumber constNumberInstruction = code.createNumberConstant(value, type);
// Note that we only keep position info for throwing instructions in release mode.
constNumberInstruction.setPosition(options.debug ? current.getPosition() : Position.none());
add(constNumberInstruction);
@@ -280,7 +272,7 @@
@Override
public void replaceCurrentInstructionWithThrowNull(
- AppView<? extends AppInfoWithSubtyping> appView,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
IRCode code,
ListIterator<BasicBlock> blockIterator,
Set<BasicBlock> blocksToRemove,
@@ -506,9 +498,9 @@
Set<BasicBlock> blocksToRemove,
DexType downcast) {
assert blocksToRemove != null;
- DexType codeHolder = code.method.holder();
- DexType inlineeHolder = inlinee.method.holder();
- if (codeHolder != inlineeHolder && inlinee.method.isOnlyInlinedIntoNestMembers()) {
+ DexType codeHolder = code.method().holder();
+ DexType inlineeHolder = inlinee.method().holder();
+ if (codeHolder != inlineeHolder && inlinee.method().isOnlyInlinedIntoNestMembers()) {
// Should rewrite private calls to virtual calls.
assert NestUtils.sameNest(codeHolder, inlineeHolder, appView);
NestUtils.rewriteNestCallsForInlining(inlinee, codeHolder, appView);
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
index bb5788b..c2d5232 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
@@ -139,7 +139,7 @@
@Override
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
- return !instructionMayHaveSideEffects(appView, code.method.holder());
+ return !instructionMayHaveSideEffects(appView, code.method().holder());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
index 18f8326..888536f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -232,10 +232,6 @@
assert isFieldGet();
DexEncodedField field = appView.appInfo().resolveField(getField());
if (field != null) {
- DexClass holder = appView.definitionFor(field.holder());
- if (holder != null && holder.isLibraryClass() && field.isStatic() && field.isFinal()) {
- return appView.abstractValueFactory().createSingleFieldValue(field.field);
- }
return field.getOptimizationInfo().getAbstractValue();
}
return UnknownValue.getInstance();
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index 064842a..53b9336 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -6,7 +6,7 @@
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -104,7 +104,7 @@
// use odd instruction numbers for the insertion of moves during spilling.
public static final int INSTRUCTION_NUMBER_DELTA = 2;
- public final DexEncodedMethod method;
+ private final DexEncodedMethod method;
public LinkedList<BasicBlock> blocks;
public final ValueNumberGenerator valueNumberGenerator;
@@ -145,6 +145,10 @@
return metadata;
}
+ public DexEncodedMethod method() {
+ return method;
+ }
+
public BasicBlock entryBlock() {
return blocks.getFirst();
}
@@ -566,7 +570,8 @@
return true;
}
- public boolean hasNoVerticallyMergedClasses(AppView<? extends AppInfoWithSubtyping> appView) {
+ public boolean hasNoVerticallyMergedClasses(
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
VerticallyMergedClasses verticallyMergedClasses = appView.verticallyMergedClasses();
if (verticallyMergedClasses == null) {
return true;
@@ -783,7 +788,7 @@
for (BasicBlock block : blocks) {
assert block.consistentBlockInstructions(
argumentsAllowed,
- options.debug || method.getOptimizationInfo().isReachabilitySensitive());
+ options.debug || method().getOptimizationInfo().isReachabilitySensitive());
argumentsAllowed = false;
}
return true;
@@ -1060,12 +1065,13 @@
}
}
assert arguments.size()
- == method.method.getArity() + ((method.accessFlags.isStatic() || ignoreReceiver) ? 0 : 1);
+ == method().method.getArity()
+ + ((method().accessFlags.isStatic() || ignoreReceiver) ? 0 : 1);
return arguments;
}
public Value getThis() {
- if (method.accessFlags.isStatic()) {
+ if (method().accessFlags.isStatic()) {
return null;
}
Instruction firstArg = entryBlock().iterator().nextUntil(Instruction::isArgument);
@@ -1083,14 +1089,20 @@
return createValue(typeLattice, null);
}
+ public ConstNumber createNumberConstant(long value, TypeElement type) {
+ return createNumberConstant(value, type, null);
+ }
+
+ public ConstNumber createNumberConstant(long value, TypeElement type, DebugLocalInfo local) {
+ return new ConstNumber(createValue(type, local), value);
+ }
+
public ConstNumber createDoubleConstant(double value, DebugLocalInfo local) {
- Value out = createValue(TypeElement.getDouble(), local);
- return new ConstNumber(out, Double.doubleToLongBits(value));
+ return createNumberConstant(Double.doubleToLongBits(value), TypeElement.getDouble(), local);
}
public ConstNumber createFloatConstant(float value, DebugLocalInfo local) {
- Value out = createValue(TypeElement.getFloat(), local);
- return new ConstNumber(out, Float.floatToIntBits(value));
+ return createNumberConstant(Float.floatToIntBits(value), TypeElement.getFloat(), local);
}
public ConstNumber createIntConstant(int value) {
@@ -1098,13 +1110,11 @@
}
public ConstNumber createIntConstant(int value, DebugLocalInfo local) {
- Value out = createValue(TypeElement.getInt(), local);
- return new ConstNumber(out, value);
+ return createNumberConstant(value, TypeElement.getInt(), local);
}
public ConstNumber createLongConstant(long value, DebugLocalInfo local) {
- Value out = createValue(TypeElement.getLong(), local);
- return new ConstNumber(out, value);
+ return createNumberConstant(value, TypeElement.getLong(), local);
}
public ConstString createStringConstant(AppView<?> appView, DexString value) {
@@ -1131,13 +1141,11 @@
}
public ConstNumber createConstNull() {
- Value out = createValue(TypeElement.getNull());
- return new ConstNumber(out, 0);
+ return createNumberConstant(0, TypeElement.getNull());
}
public ConstNumber createConstNull(DebugLocalInfo local) {
- Value out = createValue(TypeElement.getNull(), local);
- return new ConstNumber(out, 0);
+ return createNumberConstant(0, TypeElement.getNull(), local);
}
public boolean doAllThrowingInstructionsHavePositions() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
index 03ca81d..b9cc832 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
@@ -5,11 +5,12 @@
package com.android.tools.r8.ir.code;
import com.android.tools.r8.errors.Unimplemented;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.utils.InternalOptions;
import java.util.ListIterator;
import java.util.NoSuchElementException;
@@ -29,13 +30,9 @@
}
@Override
- public Value insertConstNullInstruction(IRCode code, InternalOptions options) {
- return instructionIterator.insertConstNullInstruction(code, options);
- }
-
- @Override
- public Value insertConstIntInstruction(IRCode code, InternalOptions options, int value) {
- return instructionIterator.insertConstIntInstruction(code, options, value);
+ public Value insertConstNumberInstruction(
+ IRCode code, InternalOptions options, long value, TypeElement type) {
+ return instructionIterator.insertConstNumberInstruction(code, options, value, type);
}
@Override
@@ -62,7 +59,7 @@
@Override
public void replaceCurrentInstructionWithThrowNull(
- AppView<? extends AppInfoWithSubtyping> appView,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
IRCode code,
ListIterator<BasicBlock> blockIterator,
Set<BasicBlock> blocksToRemove,
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index 0740db4..3050da7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -127,7 +127,7 @@
// * IncompatibleClassChangeError (instance-* instruction for static fields)
// * IllegalAccessError (not visible from the access context)
// * NullPointerException (null receiver)
- return !instructionMayHaveSideEffects(appView, code.method.holder());
+ return !instructionMayHaveSideEffects(appView, code.method().holder());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index 58bcd28..fd76ad3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -142,7 +142,7 @@
// * IllegalAccessError (not visible from the access context)
// * NullPointerException (null receiver)
// * not read at all
- boolean haveSideEffects = instructionMayHaveSideEffects(appView, code.method.holder());
+ boolean haveSideEffects = instructionMayHaveSideEffects(appView, code.method().holder());
assert appView.enableWholeProgramOptimizations() || haveSideEffects
: "Expected instance-put instruction to have side effects in D8";
return !haveSideEffects;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
index 371438e..a546ee3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -5,11 +5,12 @@
package com.android.tools.r8.ir.code;
import com.android.tools.r8.errors.Unimplemented;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Sets;
import java.util.ListIterator;
@@ -61,9 +62,17 @@
// Intentionally empty.
}
- Value insertConstNullInstruction(IRCode code, InternalOptions options);
+ default Value insertConstNullInstruction(IRCode code, InternalOptions options) {
+ return insertConstNumberInstruction(code, options, 0, TypeElement.getNull());
+ }
- Value insertConstIntInstruction(IRCode code, InternalOptions options, int value);
+ default Value insertConstIntInstruction(IRCode code, InternalOptions options, int value) {
+ return insertConstNumberInstruction(code, options, value, TypeElement.getInt());
+ }
+
+ // This method can be used for any numeric constant, but also for null (value 0, null type).
+ Value insertConstNumberInstruction(
+ IRCode code, InternalOptions options, long value, TypeElement type);
Value insertConstStringInstruction(AppView<?> appView, IRCode code, DexString value);
@@ -75,7 +84,7 @@
/**
* Replace the current instruction with null throwing instructions.
*
- * @param appView with subtype info through which we can test if the guard is subtype of NPE.
+ * @param appView with hierarchy info through which we can test if the guard is subtype of NPE.
* @param code the IR code for the block this iterator originates from.
* @param blockIterator basic block iterator used to iterate the blocks.
* @param blocksToRemove set passed where blocks that were detached from the graph, but not
@@ -87,7 +96,7 @@
* @param affectedValues set passed where values depending on detached blocks will be added.
*/
void replaceCurrentInstructionWithThrowNull(
- AppView<? extends AppInfoWithSubtyping> appView,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
IRCode code,
ListIterator<BasicBlock> blockIterator,
Set<BasicBlock> blocksToRemove,
diff --git a/src/main/java/com/android/tools/r8/ir/code/Invoke.java b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
index 05e80d1..8fad9af 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Invoke.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
@@ -195,12 +195,12 @@
if (outType.isPrimitiveType()) {
return false;
}
- if (appView.appInfo().hasSubtyping()) {
+ if (appView.appInfo().hasLiveness()) {
if (outType.isClassType()
&& root.getType().isClassType()
&& appView
.appInfo()
- .withSubtyping()
+ .withLiveness()
.inDifferentHierarchy(
outType.asClassType().getClassType(),
root.getType().asClassType().getClassType())) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
index 9ee1988..c3fec64 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -63,11 +63,11 @@
@Override
public TypeElement evaluate(AppView<?> appView) {
TypeElement returnType = super.evaluate(appView);
- if (!appView.appInfo().hasSubtyping()) {
+ if (!appView.appInfo().hasLiveness()) {
return returnType;
}
List<DexType> lambdaInterfaces =
- LambdaDescriptor.getInterfaces(callSite, appView.appInfo().withSubtyping());
+ LambdaDescriptor.getInterfaces(callSite, appView.appInfo().withClassHierarchy());
if (lambdaInterfaces == null || lambdaInterfaces.isEmpty()) {
return returnType;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index d6aaf36..e492db1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -217,7 +217,7 @@
@Override
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
- DexEncodedMethod method = code.method;
+ DexEncodedMethod method = code.method();
if (instructionMayHaveSideEffects(appView, method.holder())) {
return false;
}
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 7c34ec5..78d3a69 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
@@ -104,7 +104,6 @@
&& !appView
.appInfo()
.isSubtype(refinedReceiverLowerBound.type, refinedReceiverUpperBound.type)) {
- // We cannot trust the lower bound.
refinedReceiverLowerBound = null;
}
}
@@ -128,7 +127,7 @@
}
Set<DexEncodedMethod> result = Sets.newIdentityHashSet();
lookupResult.forEach(
- methodTarget -> result.add(methodTarget.getMethod()),
+ methodTarget -> result.add(methodTarget.getDefinition()),
lambda -> {
// TODO(b/150277553): Support lambda targets.
});
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 a5c4e81..5ed1539 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
@@ -83,6 +83,7 @@
DexType refinedReceiverType =
TypeAnalysis.getRefinedReceiverType(appViewWithLiveness, this);
assert receiverLowerBoundType.getClassType() == refinedReceiverType
+ || appView.options().testing.allowTypeErrors
|| receiverLowerBoundType.isBasedOnMissingClass(appViewWithLiveness)
|| upperBoundAssumedByCallSiteOptimizationAndNoLongerInstantiated(
appViewWithLiveness, refinedReceiverType, receiverLowerBoundType.getClassType())
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
index 41fac8c..4006bd7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
@@ -184,7 +184,7 @@
@Override
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
- return !instructionMayHaveSideEffects(appView, code.method.holder());
+ return !instructionMayHaveSideEffects(appView, code.method().holder());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
index b285dd6..3f5a1ab 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
@@ -197,7 +197,7 @@
@Override
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
- return !instructionMayHaveSideEffects(appView, code.method.holder());
+ return !instructionMayHaveSideEffects(appView, code.method().holder());
}
@Override
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 01a6a76..9a9f727 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
@@ -215,6 +215,6 @@
@Override
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
- return !instructionMayHaveSideEffects(appView, code.method.holder());
+ return !instructionMayHaveSideEffects(appView, code.method().holder());
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index 92eb384..b9a0765 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -205,6 +205,6 @@
@Override
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
- return !instructionMayHaveSideEffects(appView, code.method.holder());
+ return !instructionMayHaveSideEffects(appView, code.method().holder());
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
index 15700bf..d73f8df 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
@@ -4,11 +4,12 @@
package com.android.tools.r8.ir.code;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.utils.InternalOptions;
import java.util.ListIterator;
import java.util.Set;
@@ -42,13 +43,9 @@
}
@Override
- public Value insertConstNullInstruction(IRCode code, InternalOptions options) {
- return currentBlockIterator.insertConstNullInstruction(code, options);
- }
-
- @Override
- public Value insertConstIntInstruction(IRCode code, InternalOptions options, int value) {
- return currentBlockIterator.insertConstIntInstruction(code, options, value);
+ public Value insertConstNumberInstruction(
+ IRCode code, InternalOptions options, long value, TypeElement type) {
+ return currentBlockIterator.insertConstNumberInstruction(code, options, value, type);
}
@Override
@@ -70,7 +67,7 @@
@Override
public void replaceCurrentInstructionWithThrowNull(
- AppView<? extends AppInfoWithSubtyping> appView,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
IRCode code,
ListIterator<BasicBlock> blockIterator,
Set<BasicBlock> blocksToRemove,
diff --git a/src/main/java/com/android/tools/r8/ir/code/MoveException.java b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
index e9cd95f..9f21f17 100644
--- a/src/main/java/com/android/tools/r8/ir/code/MoveException.java
+++ b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
@@ -80,7 +80,8 @@
@Override
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
- return !(appView.options().debug || code.method.getOptimizationInfo().isReachabilitySensitive())
+ return !(appView.options().debug
+ || code.method().getOptimizationInfo().isReachabilitySensitive())
&& appView.options().isGeneratingDex();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
index 87643df..fd6cf65 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
@@ -151,6 +151,6 @@
@Override
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
- return !instructionMayHaveSideEffects(appView, code.method.holder());
+ return !instructionMayHaveSideEffects(appView, code.method().holder());
}
}
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 1090554..e30cdf1 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
@@ -165,7 +165,7 @@
}
// Verify that the instruction does not lead to an IllegalAccessError.
- if (appView.appInfo().hasSubtyping()
+ if (appView.appInfo().hasLiveness()
&& !isMemberVisibleFromOriginalContext(
appView, context, definition.type, definition.accessFlags)) {
return true;
@@ -196,7 +196,7 @@
@Override
public boolean canBeDeadCode(AppView<?> appView, IRCode code) {
- return !instructionMayHaveSideEffects(appView, code.method.holder());
+ return !instructionMayHaveSideEffects(appView, code.method().holder());
}
public void markNoSpilling() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
index 26e808f..a9365d8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Phi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.cf.TypeVerificationHelper;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.InvalidDebugInfoException;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexMethod;
@@ -407,7 +407,8 @@
}
@Override
- public TypeElement getDynamicUpperBoundType(AppView<? extends AppInfoWithSubtyping> appView) {
+ public TypeElement getDynamicUpperBoundType(
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
Set<Phi> reachablePhis = SetUtils.newIdentityHashSet(this);
Deque<Phi> worklist = DequeUtils.newArrayDeque(this);
while (!worklist.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index 84a86f2..de66cba 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -74,12 +74,12 @@
if (outType.isPrimitiveType()) {
return false;
}
- if (appView.appInfo().hasSubtyping()) {
+ if (appView.appInfo().hasLiveness()) {
if (outType.isClassType()
&& root.getType().isClassType()
&& appView
.appInfo()
- .withSubtyping()
+ .withLiveness()
.inDifferentHierarchy(
outType.asClassType().getClassType(),
root.getType().asClassType().getClassType())) {
@@ -153,7 +153,7 @@
// * IncompatibleClassChangeError (static-* instruction for instance fields)
// * IllegalAccessError (not visible from the access context)
// * side-effects in <clinit>
- return !instructionMayHaveSideEffects(appView, code.method.holder());
+ return !instructionMayHaveSideEffects(appView, code.method().holder());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index 7249d94..78d4de9 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -139,7 +139,7 @@
// * IllegalAccessError (not visible from the access context)
// * side-effects in <clinit>
// * not read _globally_
- boolean haveSideEffects = instructionMayHaveSideEffects(appView, code.method.holder());
+ boolean haveSideEffects = instructionMayHaveSideEffects(appView, code.method().holder());
assert appView.enableWholeProgramOptimizations() || haveSideEffects
: "Expected static-put instruction to have side effects in D8";
return !haveSideEffects;
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 87ee96f..bbe7f7f 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
@@ -14,7 +14,7 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexClass;
@@ -1132,7 +1132,8 @@
return type;
}
- public TypeElement getDynamicUpperBoundType(AppView<? extends AppInfoWithSubtyping> appView) {
+ public TypeElement getDynamicUpperBoundType(
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
Value root = getAliasedValue();
if (root.isPhi()) {
assert getSpecificAliasedValue(
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 92b23eb..49ea06a 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
@@ -222,10 +222,11 @@
lookupResult
.asLookupResultSuccess()
.forEach(
- methodTarget -> targets.add(methodTarget.getMethod()),
+ methodTarget -> targets.add(methodTarget.getDefinition()),
lambdaTarget ->
// The call target will ultimately be the implementation method.
- targets.add(lambdaTarget.getImplementationMethod().getMethod()));
+ targets.add(
+ lambdaTarget.getImplementationMethod().getDefinition()));
return targets;
}
}
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 ae2f9d7..7b15fcc 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
@@ -120,6 +120,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -186,8 +187,9 @@
private List<Action> onWaveDoneActions = null;
private final List<DexString> neverMergePrefixes;
- boolean seenNotNeverMergePrefix = false;
- boolean seenNeverMergePrefix = false;
+ // Use AtomicBoolean to satisfy TSAN checking (see b/153714743).
+ AtomicBoolean seenNotNeverMergePrefix = new AtomicBoolean();
+ AtomicBoolean seenNeverMergePrefix = new AtomicBoolean();
/**
* The argument `appView` is used to determine if whole program optimizations are allowed or not
@@ -614,12 +616,12 @@
continue;
}
if (method.holder().descriptor.startsWith(neverMergePrefix)) {
- seenNeverMergePrefix = true;
+ seenNeverMergePrefix.getAndSet(true);
} else {
- seenNotNeverMergePrefix = true;
+ seenNotNeverMergePrefix.getAndSet(true);
}
// Don't mix.
- if (seenNeverMergePrefix && seenNotNeverMergePrefix) {
+ if (seenNeverMergePrefix.get() && seenNotNeverMergePrefix.get()) {
StringBuilder message = new StringBuilder();
message
.append("Merging dex file containing classes with prefix")
@@ -813,7 +815,7 @@
outliner.applyOutliningCandidate(code);
printMethod(code, "IR after outlining (SSA)", null);
removeDeadCodeAndFinalizeIR(
- code.method, code, OptimizationFeedbackIgnore.getInstance(), Timing.empty());
+ code.method(), code, OptimizationFeedbackIgnore.getInstance(), Timing.empty());
},
executorService);
feedback.updateVisibleOptimizationInfo();
@@ -1123,7 +1125,7 @@
// TODO(b/140766440): Convert all sub steps an implementer of CodeOptimization
private Timing optimize(
IRCode code, OptimizationFeedback feedback, MethodProcessor methodProcessor) {
- DexEncodedMethod method = code.method;
+ DexEncodedMethod method = code.method();
DexProgramClass holder = asProgramClassOrNull(appView.definitionForHolder(method));
assert holder != null;
@@ -1216,7 +1218,7 @@
if (memberValuePropagation != null) {
timing.begin("Propagate member values");
- memberValuePropagation.rewriteWithConstantValues(code, method.holder());
+ memberValuePropagation.run(code);
timing.end();
}
@@ -1626,7 +1628,7 @@
timing.end();
}
- if (appView.appInfo().withLiveness().isPinned(code.method.method)) {
+ if (appView.appInfo().withLiveness().isPinned(code.method().method)) {
return;
}
@@ -1634,15 +1636,15 @@
if (method.isInitializer()) {
if (method.isClassInitializer()) {
StaticFieldValueAnalysis.run(
- appView, code, classInitializerDefaultsResult, feedback, code.method, timing);
+ appView, code, classInitializerDefaultsResult, feedback, code.method(), timing);
} else {
instanceFieldInitializationInfos =
InstanceFieldValueAnalysis.run(
- appView, code, classInitializerDefaultsResult, feedback, code.method, timing);
+ appView, code, classInitializerDefaultsResult, feedback, code.method(), timing);
}
}
methodOptimizationInfoCollector.collectMethodOptimizationInfo(
- code.method,
+ code.method(),
code,
feedback,
dynamicTypeOptimization,
@@ -1778,11 +1780,11 @@
return;
}
// Only constructors.
- if (!code.method.isInstanceInitializer()) {
+ if (!code.method().isInstanceInitializer()) {
return;
}
// Only constructors with certain signatures.
- DexTypeList paramTypes = code.method.method.proto.parameters;
+ DexTypeList paramTypes = code.method().method.proto.parameters;
if (paramTypes.size() != 3 ||
paramTypes.values[0] != options.itemFactory.doubleType ||
paramTypes.values[1] != options.itemFactory.doubleType ||
@@ -1964,7 +1966,7 @@
printer.end("cfg");
}
if (options.extensiveLoggingFilter.size() > 0
- && options.extensiveLoggingFilter.contains(code.method.method.toSourceString())) {
+ && options.extensiveLoggingFilter.contains(code.method().method.toSourceString())) {
String current = code.toString();
System.out.println();
System.out.println("-----------------------------------------------------------------------");
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 21228a9..e6f5e43 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -31,7 +31,7 @@
import static com.android.tools.r8.ir.code.Opcodes.STATIC_PUT;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
@@ -100,12 +100,12 @@
public class LensCodeRewriter {
- private final AppView<? extends AppInfoWithSubtyping> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final EnumUnboxer enumUnboxer;
private final Map<DexProto, DexProto> protoFixupCache = new ConcurrentHashMap<>();
- LensCodeRewriter(AppView<? extends AppInfoWithSubtyping> appView, EnumUnboxer enumUnboxer) {
+ LensCodeRewriter(AppView<? extends AppInfoWithClassHierarchy> appView, EnumUnboxer enumUnboxer) {
this.appView = appView;
this.enumUnboxer = enumUnboxer;
}
@@ -499,7 +499,7 @@
if (ret.isReturnVoid()) {
break;
}
- DexType returnType = code.method.method.proto.returnType;
+ DexType returnType = code.method().method.proto.returnType;
Value retValue = ret.returnValue();
DexType initialType =
retValue.getType().isPrimitiveType()
@@ -551,10 +551,10 @@
&& initialValue.definition.asConstNumber().isZero()
&& defaultValueHasChanged(oldType, newType)) {
iterator.previous();
- // TODO(b/150188380): Add API to insert a const instruction with a type lattice.
- Value rewrittenDefaultValue = iterator.insertConstIntInstruction(code, appView.options(), 0);
+ Value rewrittenDefaultValue =
+ iterator.insertConstNumberInstruction(
+ code, appView.options(), 0, defaultValueLatticeElement(newType));
iterator.next();
- rewrittenDefaultValue.setType(defaultValueLatticeElement(newType));
return rewrittenDefaultValue;
}
return initialValue;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/TypeConstraintResolver.java b/src/main/java/com/android/tools/r8/ir/conversion/TypeConstraintResolver.java
index 58f7155..8279864 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/TypeConstraintResolver.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/TypeConstraintResolver.java
@@ -152,7 +152,7 @@
+ ", its imprecise type is: "
+ stillImprecise.get(0).getType(),
code.origin,
- new MethodPosition(code.method.method)));
+ new MethodPosition(code.method().method)));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 706cfa0..e9aed7f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -159,7 +159,7 @@
if (provider.requiresGenerationOfCode()) {
DexMethod newMethod = provider.provideMethod(appView);
methodProviders.putIfAbsent(newMethod, provider);
- holders.add(code.method.holder());
+ holders.add(code.method().holder());
}
}
if (!affectedValues.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index 82c7c6a..56c1ff5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -300,7 +300,7 @@
return;
}
- DexEncodedMethod target = virtualDispatchTarget.getMethod();
+ DexEncodedMethod target = virtualDispatchTarget.getDefinition();
DexClass targetHolder = virtualDispatchTarget.getHolder();
// Don-t forward if the target is explicitly marked as 'dont-rewrite'
if (dontRewrite(targetHolder, target)) {
@@ -330,7 +330,7 @@
DexClassAndMethod result =
appView.appInfo().lookupMaximallySpecificMethod(libraryHolder, method);
if (result != null && rewriter.isEmulatedInterface(result.getHolder().type)) {
- addForward.accept(result.getHolder(), result.getMethod());
+ addForward.accept(result.getHolder(), result.getDefinition());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
index 12e4c5e..b9913cc 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
@@ -106,14 +106,14 @@
public void desugar(IRCode code) {
- if (wrapperSynthesizor.hasSynthesized(code.method.holder())) {
+ if (wrapperSynthesizor.hasSynthesized(code.method().holder())) {
return;
}
if (!canGenerateWrappersAndCallbacks()) {
- assert validateCallbackWasGeneratedInEnqueuer(code.method);
+ assert validateCallbackWasGeneratedInEnqueuer(code.method());
} else {
- registerCallbackIfRequired(code.method);
+ registerCallbackIfRequired(code.method());
}
ListIterator<BasicBlock> blockIterator = code.listIterator();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
index baa6317..1590484 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
@@ -32,6 +32,7 @@
private final AndroidApiLevel requiredCompilationAPILevel;
private final boolean libraryCompilation;
private final String synthesizedLibraryClassesPackagePrefix;
+ private final String identifier;
private final Map<String, String> rewritePrefix;
private final Map<DexType, DexType> emulateLibraryInterface;
private final Map<DexString, Map<DexType, DexType>> retargetCoreLibMember;
@@ -50,6 +51,7 @@
AndroidApiLevel.B,
true,
FALL_BACK_SYNTHESIZED_CLASSES_PACKAGE_PREFIX,
+ "testingOnlyVersion",
prefix,
ImmutableMap.of(),
ImmutableMap.of(),
@@ -64,6 +66,7 @@
AndroidApiLevel.B,
false,
FALL_BACK_SYNTHESIZED_CLASSES_PACKAGE_PREFIX,
+ null,
ImmutableMap.of(),
ImmutableMap.of(),
ImmutableMap.of(),
@@ -77,6 +80,7 @@
AndroidApiLevel requiredCompilationAPILevel,
boolean libraryCompilation,
String packagePrefix,
+ String identifier,
Map<String, String> rewritePrefix,
Map<DexType, DexType> emulateLibraryInterface,
Map<DexString, Map<DexType, DexType>> retargetCoreLibMember,
@@ -87,6 +91,7 @@
this.requiredCompilationAPILevel = requiredCompilationAPILevel;
this.libraryCompilation = libraryCompilation;
this.synthesizedLibraryClassesPackagePrefix = packagePrefix;
+ this.identifier = identifier;
this.rewritePrefix = rewritePrefix;
this.emulateLibraryInterface = emulateLibraryInterface;
this.retargetCoreLibMember = retargetCoreLibMember;
@@ -114,6 +119,10 @@
return synthesizedLibraryClassesPackagePrefix;
}
+ public String getIdentifier() {
+ return identifier;
+ }
+
public Map<String, String> getRewritePrefix() {
return rewritePrefix;
}
@@ -164,6 +173,7 @@
private boolean libraryCompilation = false;
private String synthesizedLibraryClassesPackagePrefix =
FALL_BACK_SYNTHESIZED_CLASSES_PACKAGE_PREFIX;
+ private String identifier;
private Map<String, String> rewritePrefix = new HashMap<>();
private Map<DexType, DexType> emulateLibraryInterface = new HashMap<>();
private Map<DexString, Map<DexType, DexType>> retargetCoreLibMember = new IdentityHashMap<>();
@@ -177,8 +187,12 @@
}
public Builder setSynthesizedLibraryClassesPackagePrefix(String prefix) {
- String replace = prefix.replace('.', '/');
- this.synthesizedLibraryClassesPackagePrefix = replace;
+ this.synthesizedLibraryClassesPackagePrefix = prefix.replace('.', '/');
+ return this;
+ }
+
+ public Builder setDesugaredLibraryIdentifier(String identifier) {
+ this.identifier = identifier;
return this;
}
@@ -268,6 +282,7 @@
requiredCompilationAPILevel,
libraryCompilation,
synthesizedLibraryClassesPackagePrefix,
+ identifier,
ImmutableMap.copyOf(rewritePrefix),
ImmutableMap.copyOf(emulateLibraryInterface),
ImmutableMap.copyOf(retargetCoreLibMember),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
index b66432e..a659cc6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
@@ -19,7 +19,7 @@
public class DesugaredLibraryConfigurationParser {
- private static final int MAX_SUPPORTED_VERSION = 3;
+ private static final int MAX_SUPPORTED_VERSION = 4;
private final DesugaredLibraryConfiguration.Builder configurationBuilder;
private final Reporter reporter;
@@ -53,14 +53,14 @@
JsonParser parser = new JsonParser();
JsonObject jsonConfig = parser.parse(jsonConfigString).getAsJsonObject();
- int version = jsonConfig.get("configuration_format_version").getAsInt();
- if (version > MAX_SUPPORTED_VERSION) {
+ int formatVersion = jsonConfig.get("configuration_format_version").getAsInt();
+ if (formatVersion > MAX_SUPPORTED_VERSION) {
throw reporter.fatalError(
new StringDiagnostic(
"Unsupported desugared library configuration version, please upgrade the D8/R8"
+ " compiler."));
}
- if (version == 1) {
+ if (formatVersion == 1) {
reporter.warning(
new StringDiagnostic(
"You are using an experimental version of the desugared library configuration, "
@@ -68,6 +68,19 @@
+ "production releases and to fix this warning."));
}
+ String version = jsonConfig.get("version").getAsString();
+ String groupID;
+ String artifactID;
+ if (formatVersion < 4) {
+ groupID = "com.tools.android";
+ artifactID = "desugar_jdk_libs";
+ } else {
+ groupID = jsonConfig.get("group_id").getAsString();
+ artifactID = jsonConfig.get("artifact_id").getAsString();
+ }
+ String identifier = String.join(":", groupID, artifactID, version);
+ configurationBuilder.setDesugaredLibraryIdentifier(identifier);
+
if (jsonConfig.has("synthesized_library_classes_package_prefix")) {
configurationBuilder.setSynthesizedLibraryClassesPackagePrefix(
jsonConfig.get("synthesized_library_classes_package_prefix").getAsString());
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
index 64825c1..ecf9d2e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
@@ -135,7 +135,7 @@
appView
.appInfo()
.withClassHierarchy()
- .lookupSuperTarget(invoke.getInvokedMethod(), code.method.holder());
+ .lookupSuperTarget(invoke.getInvokedMethod(), code.method().holder());
// Final methods can be rewritten as a normal invoke.
if (dexEncodedMethod != null && !dexEncodedMethod.isFinal()) {
DexMethod retargetMethod =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 9bdc9b0..6467824 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -315,7 +315,7 @@
DexEncodedMethod dexEncodedMethod =
appView
.appInfo()
- .lookupSuperTarget(invokeSuper.getInvokedMethod(), code.method.holder());
+ .lookupSuperTarget(invokeSuper.getInvokedMethod(), code.method().holder());
if (dexEncodedMethod != null) {
DexClass dexClass = appView.definitionFor(dexEncodedMethod.holder());
if (dexClass != null && dexClass.isLibraryClass()) {
@@ -393,7 +393,7 @@
// This is a invoke-direct call to a virtual method.
instructions.replaceCurrentInstruction(
new InvokeStatic(
- defaultAsMethodOfCompanionClass(virtualTarget.getMethod().method),
+ defaultAsMethodOfCompanionClass(virtualTarget.getDefinition().method),
invokeDirect.outValue(),
invokeDirect.arguments()));
} else {
@@ -448,7 +448,7 @@
DexClassAndMethod result =
appView.appInfo().lookupMaximallySpecificMethod(dexClass, invokedMethod);
if (result != null) {
- singleTarget = result.getMethod();
+ singleTarget = result.getDefinition();
}
}
if (singleTarget == null) {
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 87c3424..88d21d4 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
@@ -592,9 +592,9 @@
@Override
DexEncodedMethod ensureAccessibility(boolean allowMethodModification) {
// We already found the static method to be called, just relax its accessibility.
- target.getMethod().accessFlags.unsetPrivate();
+ target.getDefinition().accessFlags.unsetPrivate();
if (target.getHolder().isInterface()) {
- target.getMethod().accessFlags.setPublic();
+ target.getDefinition().accessFlags.setPublic();
}
return null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
index 2ac43f0..bfc9362 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -253,24 +253,25 @@
return descriptor == MATCH_FAILED ? null : descriptor;
}
+ public static boolean isLambdaMetafactoryMethod(DexCallSite callSite, DexItemFactory factory) {
+ if (!callSite.bootstrapMethod.type.isInvokeStatic()) {
+ return false;
+ }
+ return factory.isLambdaMetafactoryMethod(callSite.bootstrapMethod.asMethod());
+ }
+
/**
* Matches call site for lambda metafactory invocation pattern and returns extracted match
* information, or MATCH_FAILED if match failed.
*/
static LambdaDescriptor infer(
DexCallSite callSite, AppInfoWithClassHierarchy appInfo, DexType invocationContext) {
- // We expect bootstrap method to be either `metafactory` or `altMetafactory` method
- // of `java.lang.invoke.LambdaMetafactory` class. Both methods are static.
- if (!callSite.bootstrapMethod.type.isInvokeStatic()) {
+ if (!isLambdaMetafactoryMethod(callSite, appInfo.dexItemFactory())) {
return LambdaDescriptor.MATCH_FAILED;
}
DexItemFactory factory = appInfo.dexItemFactory();
DexMethod bootstrapMethod = callSite.bootstrapMethod.asMethod();
- if (!factory.isLambdaMetafactoryMethod(bootstrapMethod)) {
- // It is not a lambda, thus no need to manage this call site.
- return LambdaDescriptor.MATCH_FAILED;
- }
// 'Method name' operand of the invoke-custom instruction represents
// the name of the functional interface main method.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java b/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
index 6f5c3f2..9624c2a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
@@ -167,7 +167,7 @@
}
private DexType lookup(DexType type, DexString prefixToMatch, Map<DexString, DexString> map) {
- // TODO: We could use tries instead of looking-up everywhere.
+ // TODO(b/154800164): We could use tries instead of looking-up everywhere.
for (DexString prefix : map.keySet()) {
if (prefixToMatch.startsWith(prefix)) {
DexString rewrittenTypeDescriptor =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
index e477fe3..6f3aed5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
@@ -99,7 +99,7 @@
new InvokeStatic(twrCloseResourceMethod, null, invoke.inValues()));
// Mark as a class referencing utility class.
- referencingClasses.add(appInfo.definitionFor(code.method.holder()).asProgramClass());
+ referencingClasses.add(appInfo.definitionFor(code.method().holder()).asProgramClass());
}
}
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 35895b7..312ae5d 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
@@ -6,8 +6,10 @@
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.DexMethodHandle;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ResolutionResult;
+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;
@@ -19,10 +21,12 @@
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.Value;
import com.android.tools.r8.ir.conversion.CodeOptimization;
import com.android.tools.r8.ir.conversion.PostOptimization;
+import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
import com.android.tools.r8.logging.Log;
@@ -76,7 +80,7 @@
if (mode != Mode.COLLECT) {
return;
}
- DexEncodedMethod context = code.method;
+ DexEncodedMethod context = code.method();
for (Instruction instruction : code.instructions()) {
if (!instruction.isInvokeMethod() && !instruction.isInvokeCustom()) {
continue;
@@ -109,18 +113,28 @@
recordArgumentsIfNecessary(target, invoke.inValues());
}
}
- // TODO(b/129458850): if lambda desugaring happens before IR processing, seeing invoke-custom
- // means we can't find matched methods in the app, hence safe to ignore (only for DEX).
if (instruction.isInvokeCustom()) {
- // Conservatively register argument info for all possible lambda implemented methods.
- Collection<DexEncodedMethod> targets =
- appView.appInfo().lookupLambdaImplementedMethods(
- instruction.asInvokeCustom().getCallSite());
- if (targets == null || targets.isEmpty() || hasLibraryOverrides(targets)) {
+ InvokeCustom invokeCustom = instruction.asInvokeCustom();
+ // The bootstrap method for lambda allocation is always runtime internal.
+ if (LambdaDescriptor.isLambdaMetafactoryMethod(
+ invokeCustom.getCallSite(), appView.dexItemFactory())) {
continue;
}
- for (DexEncodedMethod target : targets) {
- recordArgumentsIfNecessary(target, instruction.inValues());
+ // In other cases, if the bootstrap method is program declared it will be called. The call
+ // is with runtime provided arguments so ensure that the call-site info is TOP.
+ DexMethodHandle bootstrapMethod = invokeCustom.getCallSite().bootstrapMethod;
+ SingleResolutionResult resolution =
+ appView
+ .appInfo()
+ .resolveMethod(
+ bootstrapMethod.asMethod().holder,
+ bootstrapMethod.asMethod(),
+ bootstrapMethod.isInterface)
+ .asSingleResolution();
+ if (resolution != null && resolution.getResolvedHolder().isProgramClass()) {
+ resolution
+ .getResolvedMethod()
+ .joinCallSiteOptimizationInfo(CallSiteOptimizationInfo.TOP, appView);
}
}
}
@@ -208,7 +222,7 @@
return;
}
// TODO(b/139246447): Assert no BOTTOM left.
- if (!callSiteOptimizationInfo.hasUsefulOptimizationInfo(appView, code.method)) {
+ if (!callSiteOptimizationInfo.hasUsefulOptimizationInfo(appView, code.method())) {
return;
}
Set<Value> affectedValues = Sets.newIdentityHashSet();
@@ -231,7 +245,7 @@
if (abstractValue.isSingleValue()) {
assert appView.options().enablePropagationOfConstantsAtCallSites;
SingleValue singleValue = abstractValue.asSingleValue();
- if (singleValue.isMaterializableInContext(appView, code.method.holder())) {
+ if (singleValue.isMaterializableInContext(appView, code.method().holder())) {
Instruction replacement =
singleValue.createMaterializingInstruction(appView, code, instr);
replacement.setPosition(instr.getPosition());
@@ -282,9 +296,14 @@
}
}
}
- assert argumentsSeen == code.method.method.getArity() + (code.method.isStatic() ? 0 : 1)
- : "args: " + argumentsSeen + " != "
- + "arity: " + code.method.method.getArity() + ", static: " + code.method.isStatic();
+ assert argumentsSeen == code.method().method.getArity() + (code.method().isStatic() ? 0 : 1)
+ : "args: "
+ + argumentsSeen
+ + " != "
+ + "arity: "
+ + code.method().method.getArity()
+ + ", static: "
+ + code.method().isStatic();
// After packed Argument instructions, add Assume<?> and constant instructions.
assert !iterator.peekPrevious().isArgument();
iterator.previous();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index eb36e39..3dbe05d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexItemFactory.ThrowableMethods;
@@ -27,6 +28,8 @@
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.SingleConstClassValue;
+import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
import com.android.tools.r8.ir.code.AlwaysMaterializingNop;
import com.android.tools.r8.ir.code.ArrayLength;
import com.android.tools.r8.ir.code.ArrayPut;
@@ -75,6 +78,7 @@
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.optimize.controlflow.SwitchCaseAnalyzer;
import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOutputMode;
import com.android.tools.r8.utils.LongInterval;
@@ -250,7 +254,10 @@
// Check for the patterns 'if (x == null) throw null' and
// 'if (x == null) throw new NullPointerException()'.
if (instruction.isIf()) {
- if (appView.dexItemFactory().objectsMethods.isRequireNonNullMethod(code.method.method)) {
+ if (appView
+ .dexItemFactory()
+ .objectsMethods
+ .isRequireNonNullMethod(code.method().method)) {
continue;
}
@@ -305,7 +312,7 @@
boolean canDetachValueIsNullTarget = true;
for (Instruction i : valueIsNullTarget.instructionsBefore(throwInstruction)) {
- if (!i.isBlockLocalInstructionWithoutSideEffects(appView, code.method.holder())) {
+ if (!i.isBlockLocalInstructionWithoutSideEffects(appView, code.method().holder())) {
canDetachValueIsNullTarget = false;
break;
}
@@ -511,7 +518,7 @@
int selfRecursionFanOut = 0;
Instruction lastSelfRecursiveCall = null;
for (Instruction i : code.instructions()) {
- if (i.isInvokeMethod() && i.asInvokeMethod().getInvokedMethod() == code.method.method) {
+ if (i.isInvokeMethod() && i.asInvokeMethod().getInvokedMethod() == code.method().method) {
selfRecursionFanOut++;
lastSelfRecursiveCall = i;
}
@@ -1126,7 +1133,7 @@
BasicBlock defaultTarget = theSwitch.fallthroughBlock();
SwitchCaseEliminator eliminator = null;
BasicBlockBehavioralSubsumption behavioralSubsumption =
- new BasicBlockBehavioralSubsumption(appView, code.method.holder());
+ new BasicBlockBehavioralSubsumption(appView, code.method().holder());
// Compute the set of switch cases that can be removed.
int alwaysHitCase = -1;
@@ -1251,7 +1258,7 @@
}
// Check if the invoked method is known to return one of its arguments.
- DexEncodedMethod target = invoke.lookupSingleTarget(appView, code.method.holder());
+ DexEncodedMethod target = invoke.lookupSingleTarget(appView, code.method().holder());
if (target != null && target.getOptimizationInfo().returnsArgument()) {
int argumentIndex = target.getOptimizationInfo().getReturnedArgument();
// Replace the out value of the invoke with the argument and ignore the out value.
@@ -1373,7 +1380,7 @@
// If the cast type is not accessible in the current context, we should not remove the cast
// in order to preserve IllegalAccessError. Note that JVM and ART behave differently: see
// {@link com.android.tools.r8.ir.optimize.checkcast.IllegalAccessErrorTest}.
- if (!isTypeVisibleFromContext(appView, code.method.holder(), castType)) {
+ if (!isTypeVisibleFromContext(appView, code.method().holder(), castType)) {
return RemoveCheckCastInstructionIfTrivialResult.NO_REMOVALS;
}
@@ -1430,7 +1437,7 @@
InstanceOf instanceOf, InstructionListIterator it, IRCode code) {
// If the instance-of type is not accessible in the current context, we should not remove the
// instance-of instruction in order to preserve IllegalAccessError.
- if (!isTypeVisibleFromContext(appView, code.method.holder(), instanceOf.type())) {
+ if (!isTypeVisibleFromContext(appView, code.method().holder(), instanceOf.type())) {
return false;
}
@@ -2357,6 +2364,7 @@
if (dominatorTree.dominatedBy(block, candidate.definition.getBlock())
&& shareCatchHandlers(instruction, candidate.definition)) {
instruction.outValue().replaceUsers(candidate);
+ candidate.uniquePhiUsers().forEach(Phi::removeTrivialPhi);
eliminated = true;
iterator.removeOrReplaceByDebugLocalRead();
break; // Don't try any more candidates.
@@ -2497,14 +2505,39 @@
}
}
} else {
- DexType context = code.method.holder();
+ DexType context = code.method().holder();
AbstractValue abstractValue = lhs.getAbstractValue(appView, context);
- if (abstractValue.isSingleConstClassValue() || abstractValue.isSingleFieldValue()) {
+ if (abstractValue.isSingleConstClassValue()) {
AbstractValue otherAbstractValue = rhs.getAbstractValue(appView, context);
- if (abstractValue == otherAbstractValue) {
- simplifyIfWithKnownCondition(code, block, theIf, 0);
- } else if (otherAbstractValue.isSingleEnumValue()) {
- simplifyIfWithKnownCondition(code, block, theIf, 1);
+ if (otherAbstractValue.isSingleConstClassValue()) {
+ SingleConstClassValue singleConstClassValue =
+ abstractValue.asSingleConstClassValue();
+ SingleConstClassValue otherSingleConstClassValue =
+ otherAbstractValue.asSingleConstClassValue();
+ simplifyIfWithKnownCondition(
+ code,
+ block,
+ theIf,
+ BooleanUtils.intValue(
+ singleConstClassValue.getType() != otherSingleConstClassValue.getType()));
+ }
+ } else if (abstractValue.isSingleFieldValue()) {
+ AbstractValue otherAbstractValue = rhs.getAbstractValue(appView, context);
+ if (otherAbstractValue.isSingleFieldValue()) {
+ SingleFieldValue singleFieldValue = abstractValue.asSingleFieldValue();
+ SingleFieldValue otherSingleFieldValue = otherAbstractValue.asSingleFieldValue();
+ if (singleFieldValue.getField() == otherSingleFieldValue.getField()) {
+ simplifyIfWithKnownCondition(code, block, theIf, 0);
+ } else {
+ DexEncodedField field = appView.definitionFor(singleFieldValue.getField());
+ if (field != null && field.isEnum()) {
+ DexEncodedField otherField =
+ appView.definitionFor(otherSingleFieldValue.getField());
+ if (otherField != null && otherField.isEnum()) {
+ simplifyIfWithKnownCondition(code, block, theIf, 1);
+ }
+ }
+ }
}
}
}
@@ -2789,7 +2822,7 @@
InvokeMethod invoke = insn.asInvokeMethod();
DexEncodedMethod singleTarget =
- invoke.lookupSingleTarget(appView.withLiveness(), code.method.holder());
+ invoke.lookupSingleTarget(appView.withLiveness(), code.method().holder());
if (singleTarget == null || !singleTarget.getOptimizationInfo().neverReturnsNormally()) {
continue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
index c98c262..691de34 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
@@ -87,7 +87,7 @@
}
public void canonicalize(AppView<?> appView, IRCode code) {
- DexEncodedMethod method = code.method;
+ DexEncodedMethod method = code.method();
DexType context = method.holder();
Object2ObjectLinkedOpenCustomHashMap<Instruction, List<Value>> valuesDefinedByConstant =
new Object2ObjectLinkedOpenCustomHashMap<>(
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 d9773d9..db75756 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
@@ -3,6 +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.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.ir.optimize.inliner.InlinerUtils.addMonitorEnterValue;
import static com.android.tools.r8.ir.optimize.inliner.InlinerUtils.collectAllMonitorEnterValues;
@@ -170,8 +171,12 @@
if (options.featureSplitConfiguration != null
&& !options.featureSplitConfiguration.inSameFeatureOrBase(
singleTarget.method, method.method)) {
- whyAreYouNotInliningReporter.reportInliningAcrossFeatureSplit();
- return false;
+ // Still allow inlining if we inline from the base into a feature.
+ DexClass clazz = asProgramClassOrNull(appView.definitionFor(singleTarget.method.holder));
+ if (!options.featureSplitConfiguration.isInBase(clazz.asProgramClass())) {
+ whyAreYouNotInliningReporter.reportInliningAcrossFeatureSplit();
+ return false;
+ }
}
Set<Reason> validInliningReasons = options.testing.validInliningReasons;
@@ -446,7 +451,7 @@
// Allow inlining a constructor into a constructor of the same class, as the constructor code
// is expected to adhere to the VM specification.
DexType callerMethodHolder = method.holder();
- DexType calleeMethodHolder = inlinee.method.holder();
+ DexType calleeMethodHolder = inlinee.method().holder();
// Calling a constructor on the same class from a constructor can always be inlined.
if (method.isInstanceInitializer() && callerMethodHolder == calleeMethodHolder) {
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
index d518ef6..eac97c1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DynamicTypeOptimization.java
@@ -78,7 +78,8 @@
TypeElement.fromDexType(invokedMethod.holder, definitelyNotNull(), appView);
dynamicLowerBoundType = null;
} else {
- DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.method.holder());
+ DexEncodedMethod singleTarget =
+ invoke.lookupSingleTarget(appView, code.method().holder());
if (singleTarget == null) {
continue;
}
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 76ca615..62b5ab3 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
@@ -110,7 +110,7 @@
}
});
- DexType context = code.method.holder();
+ DexType context = code.method().holder();
// Collect invocations along with arguments.
for (BasicBlock block : code.blocks) {
for (Instruction current : block.getInstructions()) {
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 d579a34..fdb59ae 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
@@ -9,7 +9,7 @@
import com.android.tools.r8.androidapi.AvailableApiExceptions;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AccessFlags;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -22,6 +22,7 @@
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;
+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.BasicBlock;
import com.android.tools.r8.ir.code.CatchHandlers.CatchHandler;
@@ -60,6 +61,7 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IteratorUtils;
import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.SetUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
@@ -596,7 +598,7 @@
}
InlineeWithReason buildInliningIR(
- AppView<? extends AppInfoWithSubtyping> appView,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
InvokeMethod invoke,
DexEncodedMethod context,
InliningIRProvider inliningIRProvider,
@@ -737,7 +739,7 @@
}
}
- if (inliningIRProvider.shouldApplyCodeRewritings(code.method)) {
+ if (inliningIRProvider.shouldApplyCodeRewritings(code.method())) {
assert lensCodeRewriter != null;
lensCodeRewriter.rewrite(code, target);
}
@@ -1043,13 +1045,14 @@
}
classInitializationAnalysis.notifyCodeHasChanged();
- applyAssumersToInlinee(code, inlinee.code, blockIterator, block);
+ postProcessInlineeBlocks(code, inlinee.code, blockIterator, block);
// The synthetic and bridge flags are maintained only if the inlinee has also these flags.
- if (context.accessFlags.isBridge() && !inlinee.code.method.accessFlags.isBridge()) {
+ if (context.accessFlags.isBridge() && !inlinee.code.method().accessFlags.isBridge()) {
context.accessFlags.demoteFromBridge();
}
- if (context.accessFlags.isSynthetic() && !inlinee.code.method.accessFlags.isSynthetic()) {
+ if (context.accessFlags.isSynthetic()
+ && !inlinee.code.method().accessFlags.isSynthetic()) {
context.accessFlags.demoteFromSynthetic();
}
@@ -1117,62 +1120,87 @@
return null;
}
- private void applyAssumersToInlinee(
+ /** Applies member rebinding to the inlinee and inserts assume instructions. */
+ private void postProcessInlineeBlocks(
IRCode code, IRCode inlinee, ListIterator<BasicBlock> blockIterator, BasicBlock block) {
- boolean assumersEnabled =
- appView.options().enableNonNullTracking
- || appView.options().enableDynamicTypeOptimization
- || appView.options().testing.forceAssumeNoneInsertion;
- if (assumersEnabled) {
- BasicBlock state = IteratorUtils.peekNext(blockIterator);
-
- Set<BasicBlock> inlineeBlocks = Sets.newIdentityHashSet();
- inlineeBlocks.addAll(inlinee.blocks);
-
- // Introduce aliases only to the inlinee blocks.
- if (appView.options().testing.forceAssumeNoneInsertion) {
- applyAssumerToInlinee(
- new AliasIntroducer(appView), code, block, blockIterator, inlineeBlocks);
- }
-
- // Add non-null IRs only to the inlinee blocks.
- if (appView.options().enableNonNullTracking) {
- Consumer<BasicBlock> splitBlockConsumer = inlineeBlocks::add;
- Assumer nonNullTracker = new NonNullTracker(appView, splitBlockConsumer);
- applyAssumerToInlinee(nonNullTracker, code, block, blockIterator, inlineeBlocks);
- }
-
- // Add dynamic type assumptions only to the inlinee blocks.
- if (appView.options().enableDynamicTypeOptimization) {
- applyAssumerToInlinee(
- new DynamicTypeOptimization(appView), code, block, blockIterator, inlineeBlocks);
- }
-
- // Restore the old state of the iterator.
- while (blockIterator.hasPrevious() && blockIterator.previous() != state) {
- // Do nothing.
- }
- assert IteratorUtils.peekNext(blockIterator) == state;
+ InternalOptions options = appView.options();
+ boolean skip =
+ !(options.enableDynamicTypeOptimization
+ || options.enableNonNullTracking
+ || options.enableValuePropagation
+ || options.testing.forceAssumeNoneInsertion);
+ if (skip) {
+ return;
}
+
+ BasicBlock state = IteratorUtils.peekNext(blockIterator);
+
+ Set<BasicBlock> inlineeBlocks = SetUtils.newIdentityHashSet(inlinee.blocks);
+
+ // Run member value propagation on the inlinee blocks.
+ if (options.enableValuePropagation) {
+ rewindBlockIteratorToFirstInlineeBlock(blockIterator, block);
+ applyMemberValuePropagationToInlinee(code, blockIterator, block, inlineeBlocks);
+ }
+
+ // Introduce aliases only to the inlinee blocks.
+ if (options.testing.forceAssumeNoneInsertion) {
+ applyAssumerToInlinee(
+ new AliasIntroducer(appView), code, blockIterator, block, inlineeBlocks);
+ }
+
+ // Add non-null IRs only to the inlinee blocks.
+ if (options.enableNonNullTracking) {
+ Consumer<BasicBlock> splitBlockConsumer = inlineeBlocks::add;
+ Assumer nonNullTracker = new NonNullTracker(appView, splitBlockConsumer);
+ applyAssumerToInlinee(nonNullTracker, code, blockIterator, block, inlineeBlocks);
+ }
+
+ // Add dynamic type assumptions only to the inlinee blocks.
+ if (options.enableDynamicTypeOptimization) {
+ applyAssumerToInlinee(
+ new DynamicTypeOptimization(appView), code, blockIterator, block, inlineeBlocks);
+ }
+ // Restore the old state of the iterator.
+ rewindBlockIteratorToFirstInlineeBlock(blockIterator, state);
// TODO(b/72693244): need a test where refined env in inlinee affects the caller.
}
private void applyAssumerToInlinee(
Assumer assumer,
IRCode code,
- BasicBlock block,
ListIterator<BasicBlock> blockIterator,
+ BasicBlock block,
Set<BasicBlock> inlineeBlocks) {
- // Move the cursor back to where the first inlinee block was added.
- while (blockIterator.hasPrevious() && blockIterator.previous() != block) {
- // Do nothing.
- }
- assert IteratorUtils.peekNext(blockIterator) == block;
-
+ rewindBlockIteratorToFirstInlineeBlock(blockIterator, block);
assumer.insertAssumeInstructionsInBlocks(code, blockIterator, inlineeBlocks::contains);
assert !blockIterator.hasNext();
}
+ private void applyMemberValuePropagationToInlinee(
+ IRCode code,
+ ListIterator<BasicBlock> blockIterator,
+ BasicBlock block,
+ Set<BasicBlock> inlineeBlocks) {
+ assert IteratorUtils.peekNext(blockIterator) == block;
+ Set<Value> affectedValues = Sets.newIdentityHashSet();
+ new MemberValuePropagation(appView)
+ .run(code, blockIterator, affectedValues, inlineeBlocks::contains);
+ if (!affectedValues.isEmpty()) {
+ new TypeAnalysis(appView).narrowing(affectedValues);
+ }
+ assert !blockIterator.hasNext();
+ }
+
+ private void rewindBlockIteratorToFirstInlineeBlock(
+ ListIterator<BasicBlock> blockIterator, BasicBlock firstInlineeBlock) {
+ // Move the cursor back to where the first inlinee block was added.
+ while (blockIterator.hasPrevious() && blockIterator.previous() != firstInlineeBlock) {
+ // Do nothing.
+ }
+ assert IteratorUtils.peekNext(blockIterator) == firstInlineeBlock;
+ }
+
public static boolean verifyNoMethodsInlinedDueToSingleCallSite(AppView<?> appView) {
for (DexProgramClass clazz : appView.appInfo().classes()) {
for (DexEncodedMethod method : clazz.methods()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberPoolCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberPoolCollection.java
index 46ac1ce..4f699e6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberPoolCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberPoolCollection.java
@@ -111,7 +111,6 @@
abstract Runnable computeMemberPoolForClass(DexClass clazz);
- // TODO(jsjeon): maybe be part of AppInfoWithSubtyping?
private Set<DexClass> getAllSuperTypesInclusive(
DexClass subject, Predicate<DexClass> stoppingCriterion) {
Set<DexClass> superTypes = new HashSet<>();
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 bf7aa9b..e6dcbbe 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
@@ -5,6 +5,7 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
+import static com.google.common.base.Predicates.alwaysTrue;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexDefinition;
@@ -45,6 +46,7 @@
import com.google.common.collect.Sets;
import java.util.ListIterator;
import java.util.Set;
+import java.util.function.Predicate;
public class MemberValuePropagation {
@@ -209,7 +211,7 @@
if (current.isStaticGet()) {
StaticGet staticGet = current.asStaticGet();
replaceInstructionByInitClassIfPossible(
- staticGet, staticGet.getField().holder, code, iterator, code.method.holder());
+ staticGet, staticGet.getField().holder, code, iterator, code.method().holder());
}
replacement.setPosition(position);
if (block.hasCatchHandlers()) {
@@ -359,34 +361,65 @@
return;
}
- // Check if a this value is known const.
- Instruction replacement =
- target.valueAsConstInstruction(code, current.outValue().getLocalInfo(), appView);
- if (replacement != null) {
- BasicBlock block = current.getBlock();
- DexType context = code.method.holder();
- Position position = current.getPosition();
-
- // All usages are replaced by the replacement value.
- affectedValues.addAll(current.outValue().affectedValues());
- current.outValue().replaceUsers(replacement.outValue());
-
- // To preserve side effects, original field-get is replaced by an explicit null-check, if
- // the field-get instruction may only fail with an NPE, or the field-get remains as-is.
- if (current.isInstanceGet()) {
- replaceInstructionByNullCheckIfPossible(current, iterator, context);
- } else {
- replaceInstructionByInitClassIfPossible(current, target.holder(), code, iterator, context);
+ AbstractValue abstractValue;
+ if (appView.appInfo().isFieldWrittenByFieldPutInstruction(target)) {
+ abstractValue = target.getOptimizationInfo().getAbstractValue();
+ if (abstractValue.isUnknown() && !target.isStatic()) {
+ AbstractValue abstractReceiverValue =
+ current.asInstanceGet().object().getAbstractValue(appView, code.method().holder());
+ if (abstractReceiverValue.isSingleFieldValue()) {
+ abstractValue =
+ abstractReceiverValue.asSingleFieldValue().getState().getAbstractFieldValue(target);
+ }
}
+ } else if (target.isStatic()) {
+ // This is guaranteed to read the static value of the field.
+ abstractValue = target.getStaticValue().toAbstractValue(appView.abstractValueFactory());
+ // Verify that the optimization info is consistent with the static value.
+ assert target.getOptimizationInfo().getAbstractValue().isUnknown()
+ || !target.hasExplicitStaticValue()
+ || abstractValue == target.getOptimizationInfo().getAbstractValue();
+ } else {
+ // This is guaranteed to read the default value of the field.
+ abstractValue = appView.abstractValueFactory().createSingleNumberValue(0);
+ }
- // Insert the definition of the replacement.
- replacement.setPosition(position);
- if (block.hasCatchHandlers()) {
- iterator.split(code, blocks).listIterator(code).add(replacement);
- } else {
- iterator.add(replacement);
+ if (abstractValue.isSingleValue()) {
+ SingleValue singleValue = abstractValue.asSingleValue();
+ if (singleValue.isSingleFieldValue()
+ && singleValue.asSingleFieldValue().getField() == field) {
+ return;
}
- feedback.markFieldAsPropagated(target);
+ if (singleValue.isMaterializableInContext(appView, code.method().holder())) {
+ BasicBlock block = current.getBlock();
+ DexType context = code.method().holder();
+ Position position = current.getPosition();
+
+ // All usages are replaced by the replacement value.
+ Instruction replacement =
+ singleValue.createMaterializingInstruction(appView, code, current);
+ affectedValues.addAll(current.outValue().affectedValues());
+ current.outValue().replaceUsers(replacement.outValue());
+
+ // To preserve side effects, original field-get is replaced by an explicit null-check, if
+ // the field-get instruction may only fail with an NPE, or the field-get remains as-is.
+ if (current.isInstanceGet()) {
+ replaceInstructionByNullCheckIfPossible(current, iterator, context);
+ } else {
+ assert current.isStaticGet();
+ replaceInstructionByInitClassIfPossible(
+ current, target.holder(), code, iterator, context);
+ }
+
+ // Insert the definition of the replacement.
+ replacement.setPosition(position);
+ if (block.hasCatchHandlers()) {
+ iterator.split(code, blocks).listIterator(code).add(replacement);
+ } else {
+ iterator.add(replacement);
+ }
+ feedback.markFieldAsPropagated(target);
+ }
}
}
@@ -460,7 +493,7 @@
return;
}
- replaceInstructionByNullCheckIfPossible(current, iterator, code.method.holder());
+ replaceInstructionByNullCheckIfPossible(current, iterator, code.method().holder());
}
private void replaceStaticPutByInitClassIfNeverRead(
@@ -475,7 +508,7 @@
}
replaceInstructionByInitClassIfPossible(
- current, field.holder(), code, iterator, code.method.holder());
+ current, field.holder(), code, iterator, code.method().holder());
}
/**
@@ -483,25 +516,39 @@
*
* <p>Also assigns value ranges to values where possible.
*/
- public void rewriteWithConstantValues(IRCode code, DexType callingContext) {
+ public void run(IRCode code) {
IRMetadata metadata = code.metadata();
if (!metadata.mayHaveFieldInstruction() && !metadata.mayHaveInvokeMethod()) {
return;
}
-
Set<Value> affectedValues = Sets.newIdentityHashSet();
- ListIterator<BasicBlock> blocks = code.listIterator();
- while (blocks.hasNext()) {
- BasicBlock block = blocks.next();
+ run(code, code.listIterator(), affectedValues, alwaysTrue());
+ if (!affectedValues.isEmpty()) {
+ new TypeAnalysis(appView).narrowing(affectedValues);
+ }
+ assert code.isConsistentSSA();
+ }
+
+ public void run(
+ IRCode code,
+ ListIterator<BasicBlock> blockIterator,
+ Set<Value> affectedValues,
+ Predicate<BasicBlock> blockTester) {
+ DexType context = code.method().holder();
+ while (blockIterator.hasNext()) {
+ BasicBlock block = blockIterator.next();
+ if (!blockTester.test(block)) {
+ continue;
+ }
InstructionListIterator iterator = block.listIterator(code);
while (iterator.hasNext()) {
Instruction current = iterator.next();
if (current.isInvokeMethod()) {
rewriteInvokeMethodWithConstantValues(
- code, callingContext, affectedValues, blocks, iterator, current.asInvokeMethod());
+ code, context, affectedValues, blockIterator, iterator, current.asInvokeMethod());
} else if (current.isFieldGet()) {
rewriteFieldGetWithConstantValues(
- code, affectedValues, blocks, iterator, current.asFieldInstruction());
+ code, affectedValues, blockIterator, iterator, current.asFieldInstruction());
} else if (current.isInstancePut()) {
replaceInstancePutByNullCheckIfNeverRead(code, iterator, current.asInstancePut());
} else if (current.isStaticPut()) {
@@ -509,9 +556,5 @@
}
}
}
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
- assert code.isConsistentSSA();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java b/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java
index 508e394..3cd05c5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NestUtils.java
@@ -46,7 +46,7 @@
InstructionListIterator iterator = code.instructionListIterator();
DexClass callerHolderClass = appView.definitionFor(callerHolder);
assert callerHolderClass != null;
- assert code.method.holder() != callerHolder;
+ assert code.method().holder() != callerHolder;
while (iterator.hasNext()) {
Instruction instruction = iterator.next();
if (instruction.isInvokeDirect()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
index bc294fa..662eb39 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
@@ -89,7 +89,8 @@
InvokeMethod invoke = current.asInvokeMethod();
DexMethod invokedMethod = invoke.getInvokedMethod();
- DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.method.holder());
+ DexEncodedMethod singleTarget =
+ invoke.lookupSingleTarget(appView, code.method().holder());
if (singleTarget != null) {
MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
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 d9ebcf6..6a05911 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
@@ -1184,7 +1184,7 @@
ListIterator<BasicBlock> blocksIterator,
BasicBlock block,
List<Integer> toRemove) {
- super(code.method, block);
+ super(code.method(), block);
this.code = code;
this.blocksIterator = blocksIterator;
this.toRemove = toRemove;
@@ -1280,9 +1280,9 @@
assert outlineMethodIdentifierGenerator == null;
outlineMethodIdentifierGenerator =
code -> {
- assert !code.method.getCode().isOutlineCode();
+ assert !code.method().getCode().isOutlineCode();
for (BasicBlock block : code.blocks) {
- new OutlineMethodIdentifier(code.method, block, candidateMap).process();
+ new OutlineMethodIdentifier(code.method(), block, candidateMap).process();
}
};
}
@@ -1295,8 +1295,8 @@
}
public void identifyOutlineSites(IRCode code) {
- assert !code.method.getCode().isOutlineCode();
- DexClass clazz = asProgramClassOrNull(appView.definitionFor(code.method.holder()));
+ assert !code.method().getCode().isOutlineCode();
+ DexClass clazz = asProgramClassOrNull(appView.definitionFor(code.method().holder()));
assert clazz != null;
if (clazz == null) {
return;
@@ -1307,7 +1307,7 @@
}
for (BasicBlock block : code.blocks) {
- new OutlineSiteIdentifier(code.method, block).process();
+ new OutlineSiteIdentifier(code.method(), block).process();
}
}
@@ -1405,7 +1405,7 @@
}
public void applyOutliningCandidate(IRCode code) {
- assert !code.method.getCode().isOutlineCode();
+ assert !code.method().getCode().isOutlineCode();
ListIterator<BasicBlock> blocksIterator = code.listIterator();
while (blocksIterator.hasNext()) {
BasicBlock block = blocksIterator.next();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
index a7e4875..ccb328e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
@@ -69,7 +69,7 @@
public RedundantFieldLoadElimination(AppView<?> appView, IRCode code) {
this.appView = appView;
- this.method = code.method;
+ this.method = code.method();
this.code = code;
}
@@ -105,7 +105,7 @@
private final SingleValue value;
private MaterializableValue(SingleValue value) {
- assert value.isMaterializableInContext(appView, method.holder());
+ assert value.isMaterializableInContext(appView.withLiveness(), method.holder());
this.value = value;
}
@@ -113,7 +113,7 @@
public void eliminateRedundantRead(InstructionListIterator it, FieldInstruction redundant) {
affectedValues.addAll(redundant.value().affectedValues());
it.replaceCurrentInstruction(
- value.createMaterializingInstruction(appView.withSubtyping(), code, redundant));
+ value.createMaterializingInstruction(appView.withClassHierarchy(), code, redundant));
}
}
@@ -374,7 +374,7 @@
activeState.putNonFinalInstanceField(fieldAndObject, new ExistingValue(value));
} else if (info.isSingleValue()) {
SingleValue value = info.asSingleValue();
- if (value.isMaterializableInContext(appView, method.holder())) {
+ if (value.isMaterializableInContext(appView.withLiveness(), method.holder())) {
Value object = invoke.getReceiver().getAliasedValue();
FieldAndObject fieldAndObject = new FieldAndObject(field.field, object);
activeState.putNonFinalInstanceField(fieldAndObject, new MaterializableValue(value));
@@ -397,7 +397,7 @@
// that we are conservative.
activeState.removeNonFinalInstanceFields(field);
} else if (instruction.isStaticPut()) {
- if (field.holder != code.method.holder()) {
+ if (field.holder != code.method().holder()) {
// Accessing a static field on a different object could cause <clinit> to run which
// could modify any static field on any other object.
activeState.clearNonFinalStaticFields();
@@ -405,7 +405,7 @@
activeState.removeNonFinalStaticField(field);
}
} else if (instruction.isStaticGet()) {
- if (field.holder != code.method.holder()) {
+ if (field.holder != code.method().holder()) {
// Accessing a static field on a different object could cause <clinit> to run which
// could modify any static field on any other object.
activeState.clearNonFinalStaticFields();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
index 3b427cb..c60da19 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
@@ -39,7 +39,7 @@
return;
}
Set<Value> affectedValues = Sets.newIdentityHashSet();
- DexType context = code.method.holder();
+ DexType context = code.method().holder();
ClassInitializationAnalysis classInitializationAnalysis =
new ClassInitializationAnalysis(appView, code);
for (BasicBlock block : code.blocks) {
@@ -68,8 +68,9 @@
ConstClass constClass = new ConstClass(value, type);
it.replaceCurrentInstruction(constClass);
if (appView.options().isGeneratingClassFiles()) {
- code.method.upgradeClassFileVersion(
- appView.options().requiredCfVersionForConstClassInstructions());
+ code.method()
+ .upgradeClassFileVersion(
+ appView.options().requiredCfVersionForConstClassInstructions());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
index 3285e2a..a2d21c3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
@@ -174,7 +174,7 @@
service -> {
DexEncodedMethod addedMethod = createSynthesizedMethod(service, classes);
if (appView.options().isGeneratingClassFiles()) {
- addedMethod.upgradeClassFileVersion(code.method.getClassFileVersion());
+ addedMethod.upgradeClassFileVersion(code.method().getClassFileVersion());
}
return addedMethod;
});
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index 6b854e6..055f575 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -7,7 +7,7 @@
import static com.android.tools.r8.ir.optimize.UninstantiatedTypeOptimization.Strategy.ALLOW_ARGUMENT_REMOVAL;
import static com.android.tools.r8.ir.optimize.UninstantiatedTypeOptimization.Strategy.DISALLOW_ARGUMENT_REMOVAL;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -351,7 +351,8 @@
Instruction instruction = instructionIterator.next();
if (instruction.throwsOnNullInput()) {
Value couldBeNullValue = instruction.getNonNullInput();
- if (isThrowNullCandidate(couldBeNullValue, instruction, appView, code.method.holder())) {
+ if (isThrowNullCandidate(
+ couldBeNullValue, instruction, appView, code.method().holder())) {
if (instruction.isInstanceGet() || instruction.isInstancePut()) {
++numberOfInstanceGetOrInstancePutWithNullReceiver;
} else if (instruction.isInvokeMethodWithReceiver()) {
@@ -403,7 +404,7 @@
private static boolean isThrowNullCandidate(
Value couldBeNullValue,
Instruction current,
- AppView<? extends AppInfoWithSubtyping> appView,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
DexType context) {
if (!couldBeNullValue.isAlwaysNull(appView)) {
return false;
@@ -450,7 +451,7 @@
IRCode code,
AssumeDynamicTypeRemover assumeDynamicTypeRemover,
Set<Value> affectedValues) {
- DexType context = code.method.holder();
+ DexType context = code.method().holder();
DexField field = instruction.getField();
DexType fieldType = field.type;
if (fieldType.isAlwaysNull(appView)) {
@@ -506,7 +507,7 @@
AssumeDynamicTypeRemover assumeDynamicTypeRemover,
Set<BasicBlock> blocksToBeRemoved,
Set<Value> affectedValues) {
- DexEncodedMethod target = invoke.lookupSingleTarget(appView, code.method.holder());
+ DexEncodedMethod target = invoke.lookupSingleTarget(appView, code.method().holder());
if (target == null) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index 9b443bc..798b820 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -203,13 +203,13 @@
// Assess eligibility of instance and class.
EligibilityStatus status = processor.isInstanceEligible();
if (status != EligibilityStatus.ELIGIBLE) {
- logEligibilityStatus(code.method, root, status);
+ logEligibilityStatus(code.method(), root, status);
// This root will never be inlined.
rootsIterator.remove();
continue;
}
status = processor.isClassAndUsageEligible();
- logEligibilityStatus(code.method, root, status);
+ logEligibilityStatus(code.method(), root, status);
if (status != EligibilityStatus.ELIGIBLE) {
// This root will never be inlined.
rootsIterator.remove();
@@ -220,7 +220,7 @@
InstructionOrPhi ineligibleUser = processor.areInstanceUsersEligible(defaultOracle);
if (ineligibleUser != null) {
// This root may succeed if users change in future.
- logIneligibleUser(code.method, root, ineligibleUser);
+ logIneligibleUser(code.method(), root, ineligibleUser);
continue;
}
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 7f0ad04..9a76a99 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
@@ -52,7 +52,7 @@
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
import com.android.tools.r8.ir.optimize.inliner.InliningIRProvider;
import com.android.tools.r8.ir.optimize.inliner.NopWhyAreYouNotInliningReporter;
-import com.android.tools.r8.kotlin.KotlinInfo;
+import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.Pair;
@@ -506,7 +506,8 @@
continue;
}
- DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.method.holder());
+ DexEncodedMethod singleTarget =
+ invoke.lookupSingleTarget(appView, code.method().holder());
if (singleTarget == null || !indirectMethodCallsOnInstance.contains(singleTarget)) {
throw new IllegalClassInlinerStateException();
}
@@ -1191,11 +1192,8 @@
DexType inlineeHolder = inlinee.holder();
DexClass inlineeClass = appView.definitionFor(inlineeHolder);
assert inlineeClass != null;
-
- KotlinInfo kotlinInfo = inlineeClass.getKotlinInfo();
- return kotlinInfo != null &&
- kotlinInfo.isSyntheticClass() &&
- kotlinInfo.asSyntheticClass().isLambda();
+ KotlinClassLevelInfo kotlinInfo = inlineeClass.getKotlinInfo();
+ return kotlinInfo.isSyntheticClass() && kotlinInfo.asSyntheticClass().isLambda();
}
private void markSizeForInlining(InvokeMethod invoke, DexEncodedMethod inlinee) {
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 d878322..cf9c904 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
@@ -188,7 +188,7 @@
// If dependencies is null, it means the enum is not eligible (It has been marked as
// unboxable by this thread or another one), so we do not need to record dependencies.
if (dependencies != null) {
- dependencies.add(code.method);
+ dependencies.add(code.method());
}
}
}
@@ -266,7 +266,7 @@
eligibleEnums.add(type);
}
} else if (use.isReturn()) {
- DexType returnType = code.method.method.proto.returnType;
+ DexType returnType = code.method().method.proto.returnType;
if (enumsUnboxingCandidates.containsKey(returnType)) {
eligibleEnums.add(returnType);
}
@@ -394,7 +394,7 @@
return Reason.INVALID_INVOKE_ON_ARRAY;
}
DexEncodedMethod encodedSingleTarget =
- invokeMethod.lookupSingleTarget(appView, code.method.holder());
+ invokeMethod.lookupSingleTarget(appView, code.method().holder());
if (encodedSingleTarget == null) {
return Reason.INVALID_INVOKE;
}
@@ -405,7 +405,7 @@
}
if (dexClass.isProgramClass()) {
if (dexClass.isEnum() && encodedSingleTarget.isInstanceInitializer()) {
- if (code.method.holder() == dexClass.type && code.method.isClassInitializer()) {
+ if (code.method().holder() == dexClass.type && code.method().isClassInitializer()) {
// The enum instance initializer is allowed to be called only from the enum clinit.
return Reason.ELIGIBLE;
} else {
@@ -446,8 +446,8 @@
return Reason.ELIGIBLE;
} else if (singleTarget == factory.enumMethods.constructor) {
// Enum constructor call is allowed only if first call of an enum initializer.
- if (code.method.isInstanceInitializer()
- && code.method.holder() == enumClass.type
+ if (code.method().isInstanceInitializer()
+ && code.method().holder() == enumClass.type
&& isFirstInstructionAfterArguments(invokeMethod, code)) {
return Reason.ELIGIBLE;
}
@@ -543,7 +543,7 @@
// Return is used for valueOf methods.
if (instruction.isReturn()) {
- DexType returnType = code.method.method.proto.returnType;
+ DexType returnType = code.method().method.proto.returnType;
if (returnType != enumClass.type && returnType.toBaseType(factory) != enumClass.type) {
return Reason.IMPLICIT_UP_CAST_IN_RETURN;
}
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 8f228b9..5412037 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
@@ -44,6 +44,9 @@
}
removeEnumsInAnnotations();
removePinnedCandidates();
+ if (appView.options().protoShrinking().isProtoShrinkingEnabled()) {
+ enumToUnboxCandidates.remove(appView.protoShrinker().references.methodToInvokeType);
+ }
return enumToUnboxCandidates;
}
@@ -131,7 +134,8 @@
private void removeEnumsInAnnotations() {
for (DexProgramClass clazz : appView.appInfo().classes()) {
- if (appView.appInfo().isSubtype(clazz.type, factory.annotationType)) {
+ if (clazz.isAnnotation()) {
+ assert clazz.interfaces.contains(appView.dexItemFactory().annotationType);
removeEnumsInAnnotation(clazz);
}
}
@@ -141,12 +145,10 @@
// Browse annotation values types in search for enum.
// Each annotation value is represented by a virtual method.
for (DexEncodedMethod method : clazz.virtualMethods()) {
- DexProto proto = method.method.proto;
- assert proto.parameters.isEmpty();
- DexType valueType = proto.returnType.toBaseType(appView.appInfo().dexItemFactory());
- if (valueType.isClassType()
- && enumToUnboxCandidates.containsKey(valueType)
- && appView.appInfo().isSubtype(valueType, appView.appInfo().dexItemFactory().enumType)) {
+ assert method.parameters().isEmpty()
+ || appView.options().testing.allowInjectedAnnotationMethods;
+ DexType valueType = method.returnType().toBaseType(appView.dexItemFactory());
+ if (enumToUnboxCandidates.containsKey(valueType)) {
enumUnboxer.reportFailure(valueType, Reason.ANNOTATION);
enumToUnboxCandidates.remove(valueType);
}
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 791c889..f4d341b 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
@@ -6,7 +6,7 @@
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.ir.analysis.type.Nullability;
@@ -153,7 +153,7 @@
}
public static CallSiteOptimizationInfo fromArguments(
- AppView<? extends AppInfoWithSubtyping> appView,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
DexEncodedMethod method,
List<Value> inValues) {
boolean allowConstantPropagation = appView.options().enablePropagationOfConstantsAtCallSites;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/LibraryOptimizationInfoInitializerFeedback.java b/src/main/java/com/android/tools/r8/ir/optimize/info/LibraryOptimizationInfoInitializerFeedback.java
new file mode 100644
index 0000000..ace2aa6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/LibraryOptimizationInfoInitializerFeedback.java
@@ -0,0 +1,25 @@
+// 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.optimize.info;
+
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+
+public class LibraryOptimizationInfoInitializerFeedback extends OptimizationFeedbackSimple {
+
+ private static LibraryOptimizationInfoInitializerFeedback INSTANCE =
+ new LibraryOptimizationInfoInitializerFeedback();
+
+ LibraryOptimizationInfoInitializerFeedback() {}
+
+ public static LibraryOptimizationInfoInitializerFeedback getInstance() {
+ return INSTANCE;
+ }
+
+ public void recordLibraryFieldHasAbstractValue(
+ DexEncodedField field, AbstractValue abstractValue) {
+ field.getMutableOptimizationInfo().setAbstractValue(abstractValue);
+ }
+}
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 da135b0..14a3800 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
@@ -715,7 +715,7 @@
return alwaysTriggerExpectedEffectBeforeAnythingElse(
code,
(instruction, it) -> {
- DexType context = code.method.holder();
+ DexType context = code.method().holder();
if (instruction.definitelyTriggersClassInitialization(
clazz, context, appView, DIRECTLY, AnalysisAssumption.INSTRUCTION_DOES_NOT_THROW)) {
// In order to preserve class initialization semantic, the exception must not be caught
@@ -848,7 +848,7 @@
if (isInstantiationOfNullPointerException(instr, it, appView.dexItemFactory())) {
it.next(); // Skip call to NullPointerException.<init>.
return InstructionEffect.NO_EFFECT;
- } else if (instr.throwsNpeIfValueIsNull(value, appView, code.method.holder())) {
+ } else if (instr.throwsNpeIfValueIsNull(value, appView, code.method().holder())) {
// In order to preserve NPE semantic, the exception must not be caught by any handler.
// Therefore, we must ignore this instruction if it is covered by a catch handler.
// Note: this is a conservative approach where we consider that any catch handler could
@@ -857,7 +857,7 @@
// We found a NPE check on the value.
return InstructionEffect.DESIRED_EFFECT;
}
- } else if (instr.instructionMayHaveSideEffects(appView, code.method.holder())) {
+ } else if (instr.instructionMayHaveSideEffects(appView, code.method().holder())) {
// If the current instruction is const-string, this could load the parameter name.
// Just make sure it is indeed not throwing.
if (instr.isConstString() && !instr.instructionInstanceCanThrow()) {
@@ -1198,7 +1198,7 @@
}
}
if (facts.length() > 0) {
- feedback.setNonNullParamOnNormalExits(code.method, facts);
+ feedback.setNonNullParamOnNormalExits(code.method(), facts);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
index 118c285..3237c20 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableFieldOptimizationInfo.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.ir.optimize.info;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
@@ -35,7 +35,7 @@
private TypeElement dynamicUpperBoundType = null;
public MutableFieldOptimizationInfo fixupClassTypeReferences(
- Function<DexType, DexType> mapping, AppView<? extends AppInfoWithSubtyping> appView) {
+ Function<DexType, DexType> mapping, AppView<? extends AppInfoWithClassHierarchy> appView) {
if (dynamicUpperBoundType != null) {
dynamicUpperBoundType = dynamicUpperBoundType.fixupClassTypeReferences(mapping, appView);
}
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 c03c8e0d..8942ea1 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
@@ -23,7 +23,7 @@
private static OptimizationFeedbackSimple INSTANCE = new OptimizationFeedbackSimple();
- private OptimizationFeedbackSimple() {}
+ OptimizationFeedbackSimple() {}
public static OptimizationFeedbackSimple getInstance() {
return INSTANCE;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
index 5ad6c86..c1012ca 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.ir.optimize.info;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
@@ -145,7 +145,7 @@
}
public UpdatableMethodOptimizationInfo fixupClassTypeReferences(
- Function<DexType, DexType> mapping, AppView<? extends AppInfoWithSubtyping> appView) {
+ Function<DexType, DexType> mapping, AppView<? extends AppInfoWithClassHierarchy> appView) {
if (returnsObjectWithUpperBoundType != null) {
returnsObjectWithUpperBoundType =
returnsObjectWithUpperBoundType.fixupClassTypeReferences(mapping, appView);
@@ -411,7 +411,7 @@
}
void markReturnsAbstractValue(AbstractValue value) {
- assert !abstractReturnValue.isSingleValue() || abstractReturnValue.asSingleValue() == value
+ assert !abstractReturnValue.isSingleValue() || abstractReturnValue.equals(value)
: "return single value changed from " + abstractReturnValue + " to " + value;
abstractReturnValue = value;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
index ec77b22..3bfce16 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
@@ -63,10 +63,11 @@
return new InstanceFieldTypeInitializationInfo(
dynamicLowerBoundType != null
? dynamicLowerBoundType
- .fixupClassTypeReferences(lens::lookupType, appView.withSubtyping())
+ .fixupClassTypeReferences(lens::lookupType, appView.withClassHierarchy())
.asClassType()
: null,
- dynamicUpperBoundType.fixupClassTypeReferences(lens::lookupType, appView.withSubtyping()));
+ dynamicUpperBoundType.fixupClassTypeReferences(
+ lens::lookupType, appView.withClassHierarchy()));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
index 041616c..b80283b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.ir.optimize.lambda;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
@@ -156,7 +156,7 @@
public abstract Strategy getCodeStrategy();
public abstract ThrowingConsumer<DexClass, LambdaStructureError> lambdaClassValidator(
- Kotlin kotlin, AppInfoWithSubtyping appInfo);
+ Kotlin kotlin, AppInfoWithClassHierarchy appInfo);
// Package for a lambda group class to be created in.
protected abstract String getTypePackage();
@@ -164,7 +164,7 @@
protected abstract String getGroupSuffix();
final DexProgramClass synthesizeClass(
- AppView<? extends AppInfoWithSubtyping> appView, OptimizationFeedback feedback) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, OptimizationFeedback feedback) {
assert classType == null;
assert verifyLambdaIds(true);
List<LambdaInfo> lambdas = Lists.newArrayList(this.lambdas.values());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
index 2c4315c..697638f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.ir.optimize.lambda;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.DexAnnotationSet;
@@ -35,7 +35,7 @@
}
public final DexProgramClass synthesizeClass(
- AppView<? extends AppInfoWithSubtyping> appView, OptimizationFeedback feedback) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, OptimizationFeedback feedback) {
DexType groupClassType = group.getGroupClassType();
DexType superClassType = getSuperClassType();
DexProgramClass programClass =
@@ -79,7 +79,7 @@
protected abstract DexEncodedField[] buildInstanceFields();
protected abstract DexEncodedField[] buildStaticFields(
- AppView<? extends AppInfoWithSubtyping> appView, OptimizationFeedback feedback);
+ AppView<? extends AppInfoWithClassHierarchy> appView, OptimizationFeedback feedback);
protected abstract DexTypeList buildInterfaces();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index 0bdaa57..63d2c1d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -249,8 +249,7 @@
.filter(cls -> !appView.appInfo().isPinned(cls.type))
.filter(
cls ->
- cls.hasKotlinInfo()
- && cls.getKotlinInfo().isSyntheticClass()
+ cls.getKotlinInfo().isSyntheticClass()
&& cls.getKotlinInfo().asSyntheticClass().isLambda()
&& KotlinLambdaGroupIdFactory.hasValidAnnotations(kotlin, cls)
&& (appView.options().featureSplitConfiguration == null
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
index f868855..4aafb60 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.ir.optimize.lambda.kotlin;
import com.android.tools.r8.code.ReturnVoid;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
@@ -114,7 +114,7 @@
@Override
public ThrowingConsumer<DexClass, LambdaStructureError> lambdaClassValidator(
- Kotlin kotlin, AppInfoWithSubtyping appInfo) {
+ Kotlin kotlin, AppInfoWithClassHierarchy appInfo) {
return new ClassValidator(kotlin, appInfo);
}
@@ -155,7 +155,7 @@
// Specialized class validator.
private class ClassValidator extends KotlinLambdaClassValidator {
- ClassValidator(Kotlin kotlin, AppInfoWithSubtyping appInfo) {
+ ClassValidator(Kotlin kotlin, AppInfoWithClassHierarchy appInfo) {
super(kotlin, JStyleLambdaGroup.this, appInfo);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java
index 8f26df4..e392112 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java
@@ -24,7 +24,7 @@
boolean accessRelaxed =
appView.options().getProguardConfiguration().isAccessModificationAllowed();
- assert lambda.hasKotlinInfo() && lambda.getKotlinInfo().isSyntheticClass();
+ assert lambda.getKotlinInfo().isSyntheticClass();
assert lambda.getKotlinInfo().asSyntheticClass().isJavaStyleLambda();
checkAccessFlags("class access flags", lambda.accessFlags,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
index 0295106..cc9061a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
@@ -7,7 +7,7 @@
import com.android.tools.r8.code.Const16;
import com.android.tools.r8.code.Const4;
import com.android.tools.r8.code.ReturnVoid;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
@@ -120,7 +120,7 @@
@Override
public ThrowingConsumer<DexClass, LambdaStructureError> lambdaClassValidator(
- Kotlin kotlin, AppInfoWithSubtyping appInfo) {
+ Kotlin kotlin, AppInfoWithClassHierarchy appInfo) {
return new ClassValidator(kotlin, appInfo);
}
@@ -161,7 +161,7 @@
// Specialized class validator.
private final class ClassValidator extends KotlinLambdaClassValidator {
- ClassValidator(Kotlin kotlin, AppInfoWithSubtyping appInfo) {
+ ClassValidator(Kotlin kotlin, AppInfoWithClassHierarchy appInfo) {
super(kotlin, KStyleLambdaGroup.this, appInfo);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java
index eaf154c..ea39dce 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java
@@ -24,7 +24,7 @@
boolean accessRelaxed =
appView.options().getProguardConfiguration().isAccessModificationAllowed();
- assert lambda.hasKotlinInfo() && lambda.getKotlinInfo().isSyntheticClass();
+ assert lambda.getKotlinInfo().isSyntheticClass();
assert lambda.getKotlinInfo().asSyntheticClass().isKotlinStyleLambda();
checkAccessFlags("class access flags", lambda.accessFlags,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
index 3e38f67..a956f84 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
@@ -16,7 +16,7 @@
import com.android.tools.r8.code.ReturnVoid;
import com.android.tools.r8.code.SputObject;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
@@ -54,9 +54,10 @@
final Kotlin kotlin;
private final KotlinLambdaGroup group;
- private final AppInfoWithSubtyping appInfo;
+ private final AppInfoWithClassHierarchy appInfo;
- KotlinLambdaClassValidator(Kotlin kotlin, KotlinLambdaGroup group, AppInfoWithSubtyping appInfo) {
+ KotlinLambdaClassValidator(
+ Kotlin kotlin, KotlinLambdaGroup group, AppInfoWithClassHierarchy appInfo) {
this.kotlin = kotlin;
this.group = group;
this.appInfo = appInfo;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
index 9f14329..dfd62d3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
@@ -6,7 +6,7 @@
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.DexAnnotation;
@@ -223,7 +223,7 @@
@Override
protected DexEncodedField[] buildStaticFields(
- AppView<? extends AppInfoWithSubtyping> appView, OptimizationFeedback feedback) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, OptimizationFeedback feedback) {
if (!group.isStateless()) {
return DexEncodedField.EMPTY_ARRAY;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
index 91ff482..c8e08c1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
@@ -34,7 +34,7 @@
AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
throws LambdaStructureError {
- assert lambda.hasKotlinInfo() && lambda.getKotlinInfo().isSyntheticClass();
+ assert lambda.getKotlinInfo().isSyntheticClass();
if (lambda.getKotlinInfo().asSyntheticClass().isKotlinStyleLambda()) {
return KStyleLambdaGroupIdFactory.INSTANCE.validateAndCreate(appView, kotlin, lambda);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java
index 16d514c..76e81ac 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java
@@ -70,7 +70,7 @@
InvokeMethod invoke,
Set<Value> affectedValues) {
Value argument = invoke.arguments().get(0);
- AbstractValue abstractValue = argument.getAbstractValue(appView, code.method.holder());
+ AbstractValue abstractValue = argument.getAbstractValue(appView, code.method().holder());
if (abstractValue.isSingleNumberValue()) {
instructionIterator.replaceCurrentInstructionWithStaticGet(
appView,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
index 46a1d7c..6566187 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
@@ -42,7 +42,8 @@
register(new ObjectMethodOptimizer(appView));
register(new ObjectsMethodOptimizer(appView));
register(new StringMethodOptimizer(appView));
- if (appView.appInfo().hasSubtyping() && appView.options().enableDynamicTypeOptimization) {
+ if (appView.enableWholeProgramOptimizations()
+ && appView.options().enableDynamicTypeOptimization) {
// Subtyping is required to prove the enum class is a subtype of java.lang.Enum.
register(new EnumMethodOptimizer(appView));
}
@@ -55,7 +56,7 @@
LibraryOptimizationInfoInitializer libraryOptimizationInfoInitializer =
new LibraryOptimizationInfoInitializer(appView);
- libraryOptimizationInfoInitializer.run();
+ libraryOptimizationInfoInitializer.run(finalLibraryFields);
modeledLibraryTypes.addAll(libraryOptimizationInfoInitializer.getModeledLibraryTypes());
}
@@ -113,7 +114,7 @@
Instruction instruction = instructionIterator.next();
if (instruction.isInvokeMethod()) {
InvokeMethod invoke = instruction.asInvokeMethod();
- DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.method.holder());
+ DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.method().holder());
if (singleTarget != null) {
optimizeInvoke(code, instructionIterator, invoke, singleTarget, affectedValues);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
index 4cd152f..a677b43 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryOptimizationInfoInitializer.java
@@ -5,29 +5,36 @@
package com.android.tools.r8.ir.optimize.library;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
+import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
+import com.android.tools.r8.ir.analysis.value.ObjectState;
+import com.android.tools.r8.ir.optimize.info.LibraryOptimizationInfoInitializerFeedback;
import com.google.common.collect.Sets;
import java.util.BitSet;
import java.util.Set;
public class LibraryOptimizationInfoInitializer {
+ private final AbstractValueFactory abstractValueFactory;
private final AppView<?> appView;
private final DexItemFactory dexItemFactory;
- private final OptimizationFeedbackSimple feedback = OptimizationFeedbackSimple.getInstance();
+ private final LibraryOptimizationInfoInitializerFeedback feedback =
+ LibraryOptimizationInfoInitializerFeedback.getInstance();
private final Set<DexType> modeledLibraryTypes = Sets.newIdentityHashSet();
LibraryOptimizationInfoInitializer(AppView<?> appView) {
+ this.abstractValueFactory = appView.abstractValueFactory();
this.appView = appView;
this.dexItemFactory = appView.dexItemFactory();
}
- void run() {
+ void run(Set<DexEncodedField> finalLibraryFields) {
+ modelStaticFinalLibraryFields(finalLibraryFields);
modelLibraryMethodsReturningNonNull();
modelLibraryMethodsReturningReceiver();
modelRequireNonNullMethods();
@@ -37,6 +44,15 @@
return modeledLibraryTypes;
}
+ private void modelStaticFinalLibraryFields(Set<DexEncodedField> finalLibraryFields) {
+ for (DexEncodedField field : finalLibraryFields) {
+ if (field.isStatic()) {
+ feedback.recordLibraryFieldHasAbstractValue(
+ field, abstractValueFactory.createSingleFieldValue(field.field, ObjectState.empty()));
+ }
+ }
+ }
+
private void modelLibraryMethodsReturningNonNull() {
for (DexMethod method : dexItemFactory.libraryMethodsReturningNonNull) {
DexEncodedMethod definition = lookupMethod(method);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
index 0713076..1283bf4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
@@ -47,7 +47,7 @@
private void optimizeEquals(
IRCode code, InstructionListIterator instructionIterator, InvokeMethod invoke) {
if (appView.appInfo().hasLiveness()) {
- DexType context = code.method.holder();
+ DexType context = code.method().holder();
Value first = invoke.arguments().get(0).getAliasedValue();
Value second = invoke.arguments().get(1).getAliasedValue();
if (isPrunedClassNameComparison(first, second, context)
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index c451bd7..845a01c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -353,7 +353,7 @@
OptimizationFeedback feedback) {
return (code, methodProcessor) ->
converter.collectOptimizationInfo(
- code.method,
+ code.method(),
code,
ClassInitializerDefaultsResult.empty(),
feedback,
@@ -362,7 +362,7 @@
}
private void removeCandidateInstantiation(IRCode code, MethodProcessor methodProcessor) {
- CandidateInfo candidateInfo = hostClassInits.get(code.method);
+ CandidateInfo candidateInfo = hostClassInits.get(code.method());
assert candidateInfo != null;
// Find and remove instantiation and its users.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
index cc8a24f..ae94194 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
@@ -299,7 +299,7 @@
// Find Class#get*Name() with a constant-class and replace it with a const-string if possible.
public void rewriteClassGetName(AppView<?> appView, IRCode code) {
// Conflict with {@link CodeRewriter#collectClassInitializerDefaults}.
- if (code.method.isClassInitializer()) {
+ if (code.method().isClassInitializer()) {
return;
}
Set<Value> affectedValues = Sets.newIdentityHashSet();
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index 0f6f6c9..7eddfcc 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -206,7 +206,7 @@
// There are no linked values prior to register allocation.
assert noLinkedValues();
assert code.isConsistentSSA();
- if (this.code.method.accessFlags.isBridge() && implementationIsBridge(this.code)) {
+ if (this.code.method().accessFlags.isBridge() && implementationIsBridge(this.code)) {
transformBridgeMethod();
}
computeNeedsRegister();
@@ -225,7 +225,7 @@
// and we do not actually want locals information in the output.
if (options().debug) {
computeDebugInfo(code, blocks, liveIntervals, this, liveAtEntrySets);
- } else if (code.method.getOptimizationInfo().isReachabilitySensitive()) {
+ } else if (code.method().getOptimizationInfo().isReachabilitySensitive()) {
InstructionListIterator it = code.instructionListIterator();
while (it.hasNext()) {
Instruction instruction = it.next();
@@ -604,7 +604,7 @@
if (intervals == null) {
throw new CompilationError(
"Unexpected attempt to get register for a value without a register in method `"
- + code.method.method.toSourceString()
+ + code.method().method.toSourceString()
+ "`.",
code.origin);
}
@@ -1634,8 +1634,8 @@
// Set all free positions for possible registers to max integer.
RegisterPositions freePositions = new RegisterPositions(registerConstraint + 1);
- if ((options().debug || code.method.getOptimizationInfo().isReachabilitySensitive())
- && !code.method.accessFlags.isStatic()) {
+ if ((options().debug || code.method().getOptimizationInfo().isReachabilitySensitive())
+ && !code.method().accessFlags.isStatic()) {
// If we are generating debug information or if the method is reachability sensitive,
// we pin the this value register. The debugger expects to always be able to find it in
// the input register.
@@ -2495,7 +2495,7 @@
// overwritten can therefore lead to verification errors. If we could be targeting one of these
// VMs we block the receiver register throughout the method.
if ((options().canHaveThisTypeVerifierBug() || options().canHaveThisJitCodeDebuggingBug())
- && !code.method.accessFlags.isStatic()) {
+ && !code.method().accessFlags.isStatic()) {
for (Instruction instruction : code.entryBlock().getInstructions()) {
if (instruction.isArgument() && instruction.outValue().isThis()) {
Value thisValue = instruction.outValue();
@@ -2625,7 +2625,7 @@
}
}
}
- if (options.debug || code.method.getOptimizationInfo().isReachabilitySensitive()) {
+ if (options.debug || code.method().getOptimizationInfo().isReachabilitySensitive()) {
// In debug mode, or if the method is reachability sensitive, extend the live range
// to cover the full scope of a local variable (encoded as debug values).
int number = instruction.getNumber();
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index e04f07c..9f51daa 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -40,7 +40,7 @@
import com.google.common.collect.ImmutableMap.Builder;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.util.concurrent.ExecutorService;
+import java.util.Optional;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
@@ -88,24 +88,21 @@
this.proguardMapSupplier = proguardMapSupplier;
}
- public void write(ClassFileConsumer consumer, ExecutorService executor) {
+ public void write(ClassFileConsumer consumer) {
application.timing.begin("CfApplicationWriter.write");
try {
- writeApplication(consumer, executor);
+ writeApplication(consumer);
} finally {
application.timing.end();
}
}
- private void writeApplication(ClassFileConsumer consumer, ExecutorService executor) {
- ProguardMapSupplier.ProguardMapAndId proguardMapAndId = null;
+ private void writeApplication(ClassFileConsumer consumer) {
if (proguardMapSupplier != null && options.proguardMapConsumer != null) {
- proguardMapAndId = proguardMapSupplier.getProguardMapAndId();
- if (proguardMapAndId != null) {
- marker.setPgMapId(proguardMapAndId.id);
- }
+ marker.setPgMapId(proguardMapSupplier.writeProguardMap().get());
}
- String markerString = marker.toString();
+ Optional<String> markerString =
+ marker.isRelocator() ? Optional.empty() : Optional.of(marker.toString());
for (DexProgramClass clazz : application.classes()) {
if (clazz.getSynthesizedFrom().isEmpty()
|| options.isDesugaredLibraryCompilation()
@@ -116,18 +113,16 @@
}
}
ApplicationWriter.supplyAdditionalConsumers(
- application,
- appView,
- graphLense,
- namingLens,
- options,
- proguardMapAndId == null ? null : proguardMapAndId.map);
+ application, appView, graphLense, namingLens, options);
}
- private void writeClass(DexProgramClass clazz, ClassFileConsumer consumer, String markerString) {
+ private void writeClass(
+ DexProgramClass clazz, ClassFileConsumer consumer, Optional<String> markerString) {
ClassWriter writer = new ClassWriter(0);
- int markerStringPoolIndex = writer.newConst(markerString);
- assert markerStringPoolIndex == MARKER_STRING_CONSTANT_POOL_INDEX;
+ if (markerString.isPresent()) {
+ int markerStringPoolIndex = writer.newConst(markerString.get());
+ assert markerStringPoolIndex == MARKER_STRING_CONSTANT_POOL_INDEX;
+ }
String sourceDebug = getSourceDebugExtension(clazz.annotations());
writer.visitSource(clazz.sourceFile != null ? clazz.sourceFile.toString() : null, sourceDebug);
int version = getClassFileVersion(clazz);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KmVisitorProviders.java b/src/main/java/com/android/tools/r8/kotlin/KmVisitorProviders.java
new file mode 100644
index 0000000..1ac680a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KmVisitorProviders.java
@@ -0,0 +1,98 @@
+// 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.kotlin;
+
+import kotlinx.metadata.KmAnnotation;
+import kotlinx.metadata.KmFunctionVisitor;
+import kotlinx.metadata.KmLambdaVisitor;
+import kotlinx.metadata.KmPropertyVisitor;
+import kotlinx.metadata.KmTypeAliasVisitor;
+import kotlinx.metadata.KmTypeParameterVisitor;
+import kotlinx.metadata.KmTypeVisitor;
+import kotlinx.metadata.KmValueParameterVisitor;
+import kotlinx.metadata.KmVariance;
+
+/**
+ * The reason for having these visitor providers is to make the separation of concern a bit easier
+ * while also working with the kotlinx.metadata visitors as shown by the following example:
+ *
+ * <p>Say we have the following structure KotlinTypeInfo: { TypeProjects:
+ * [KotlinTypeProjectionInfo(StarProjection)] }
+ *
+ * <p>Now the KmTypeVisitor (we use to generate the KotlinTypeInfo, has a visitProjection(int flags,
+ * KmVariance variance) generator that will return a new KmTypeVisitor, however, if the projection
+ * is a star projection, the generator visitStarProjection() should be used.
+ *
+ * <p>The information about the projection being a star projection is contained in the
+ * KotlinTypeProjectionInfo. As a result, KotlinTypeInfo should query the object for picking the
+ * right generator, the KotlinTypeProjectionInfo should return a KmTypeProjection object, or we
+ * simply capture the generators lazily (by these providers), such that the object with all the
+ * information can decide when/what object to create.
+ *
+ * <p>Another benefit of this approach than using the build in visitors is that shared structures,
+ * such as KotlinAnnotationInfo that can be on type-aliases, functions and properties will not have
+ * to take in three different type of visitors.
+ */
+public class KmVisitorProviders {
+
+ @FunctionalInterface
+ public interface KmAnnotationVisitorProvider {
+
+ void get(KmAnnotation annotation);
+ }
+
+ @FunctionalInterface
+ public interface KmFunctionVisitorProvider {
+
+ KmFunctionVisitor get(int flags, String name);
+ }
+
+ public interface KmLambdaVisitorProvider {
+
+ KmLambdaVisitor get();
+ }
+
+ @FunctionalInterface
+ public interface KmPropertyVisitorProvider {
+
+ KmPropertyVisitor get(int flags, String name, int getterFlags, int setterFlags);
+ }
+
+ @FunctionalInterface
+ public interface KmTypeAliasVisitorProvider {
+
+ KmTypeAliasVisitor get(int flags, String name);
+ }
+
+ @FunctionalInterface
+ public interface KmTypeParameterVisitorProvider {
+
+ KmTypeParameterVisitor get(int flags, String name, int id, KmVariance variance);
+ }
+
+ @FunctionalInterface
+ public interface KmTypeProjectionVisitorProvider {
+
+ KmTypeVisitor get(int flags, KmVariance variance);
+ }
+
+ @FunctionalInterface
+ public interface KmTypeStarProjectionVisitorProvider {
+
+ void get();
+ }
+
+ @FunctionalInterface
+ public interface KmTypeVisitorProvider {
+
+ KmTypeVisitor get(int flags);
+ }
+
+ @FunctionalInterface
+ public interface KmValueParameterVisitorProvider {
+
+ KmValueParameterVisitor get(int flags, String name);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
index 6fe2c18..18b3533 100644
--- a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
+++ b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.kotlin;
-import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
@@ -44,6 +44,7 @@
public static final class ClassClassifiers {
public static final String arrayBinaryName = NAME + "/Array";
+ public static final String anyName = NAME + "/Any";
}
// Mappings from JVM types to Kotlin types (of type DexType)
@@ -193,7 +194,7 @@
}
// Calculates kotlin info for a class.
- public KotlinInfo getKotlinInfo(DexClass clazz, DiagnosticsHandler reporter) {
- return KotlinClassMetadataReader.getKotlinInfo(this, clazz, reporter);
+ public KotlinClassLevelInfo getKotlinInfo(DexClass clazz, AppView<?> appView) {
+ return KotlinClassMetadataReader.getKotlinInfo(this, clazz, appView);
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java
new file mode 100644
index 0000000..47ed582
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.Map;
+import kotlinx.metadata.KmAnnotation;
+import kotlinx.metadata.KmAnnotationArgument;
+
+// Holds information about a KmAnnotation
+public class KotlinAnnotationInfo {
+
+ private static final List<KotlinAnnotationInfo> EMPTY_ANNOTATIONS = ImmutableList.of();
+
+ private final DexType annotationType;
+ // TODO(b/155053894): Model KmAnnotationArgument.
+ private final Map<String, KmAnnotationArgument<?>> arguments;
+
+ private KotlinAnnotationInfo(
+ DexType annotationType, Map<String, KmAnnotationArgument<?>> arguments) {
+ this.annotationType = annotationType;
+ this.arguments = arguments;
+ }
+
+ private static KotlinAnnotationInfo create(KmAnnotation annotation, AppView<?> appView) {
+ String descriptor = DescriptorUtils.getDescriptorFromClassBinaryName(annotation.getClassName());
+ DexType type = appView.dexItemFactory().createType(descriptor);
+ return new KotlinAnnotationInfo(type, annotation.getArguments());
+ }
+
+ static List<KotlinAnnotationInfo> create(List<KmAnnotation> annotations, AppView<?> appView) {
+ if (annotations.isEmpty()) {
+ return EMPTY_ANNOTATIONS;
+ }
+ ImmutableList.Builder<KotlinAnnotationInfo> builder = ImmutableList.builder();
+ for (KmAnnotation annotation : annotations) {
+ builder.add(create(annotation, appView));
+ }
+ return builder.build();
+ }
+
+ public void rewrite(
+ KmVisitorProviders.KmAnnotationVisitorProvider visitorProvider,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens namingLens) {
+ if (appView.appInfo().wasPruned(annotationType)) {
+ return;
+ }
+ DexString descriptor = namingLens.lookupDescriptor(annotationType);
+ String classifier = DescriptorUtils.descriptorToKotlinClassifier(descriptor.toString());
+ KmAnnotation annotation = new KmAnnotation(classifier, arguments);
+ visitorProvider.get(annotation);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
deleted file mode 100644
index 8be3f22..0000000
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClass.java
+++ /dev/null
@@ -1,201 +0,0 @@
-// Copyright (c) 2018, 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.kotlin;
-
-import static com.android.tools.r8.kotlin.Kotlin.addKotlinPrefix;
-import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.toKmType;
-import static kotlinx.metadata.Flag.IS_SEALED;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.graph.SubtypingInfo;
-import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.List;
-import kotlinx.metadata.KmClass;
-import kotlinx.metadata.KmConstructor;
-import kotlinx.metadata.KmType;
-import kotlinx.metadata.jvm.KotlinClassHeader;
-import kotlinx.metadata.jvm.KotlinClassMetadata;
-
-public class KotlinClass extends KotlinInfo<KotlinClassMetadata.Class> {
-
- KmClass kmClass;
-
- DexField companionObject = null;
- DexProgramClass hostClass = null;
-
- static KotlinClass fromKotlinClassMetadata(
- KotlinClassMetadata kotlinClassMetadata, DexClass clazz) {
- assert kotlinClassMetadata instanceof KotlinClassMetadata.Class;
- KotlinClassMetadata.Class kClass = (KotlinClassMetadata.Class) kotlinClassMetadata;
- return new KotlinClass(kClass, clazz);
- }
-
- private KotlinClass(KotlinClassMetadata.Class metadata, DexClass clazz) {
- super(metadata, clazz);
- }
-
- void foundCompanionObject(DexEncodedField companionObject) {
- // Companion cannot be nested. If this class is a host (and about to store a field that holds
- // a companion object), it should not have a host class.
- assert hostClass == null;
- this.companionObject = companionObject.field;
- }
-
- boolean hasCompanionObject() {
- return companionObject != null;
- }
-
- DexType getCompanionObjectType() {
- return hasCompanionObject() ? companionObject.type : null;
- }
-
- void linkHostClass(DexProgramClass hostClass) {
- // Companion cannot be nested. If this class is a companion object (and about to link to its
- // host class), it should not have a companion object.
- assert companionObject == null;
- this.hostClass = hostClass;
- }
-
- @Override
- void processMetadata(KotlinClassMetadata.Class metadata) {
- kmClass = metadata.toKmClass();
- }
-
- @Override
- void rewrite(AppView<AppInfoWithLiveness> appView, SubtypingInfo subtypingInfo, NamingLens lens) {
- KotlinMetadataSynthesizer synthesizer = new KotlinMetadataSynthesizer(appView, lens, this);
- if (appView.options().enableKotlinMetadataRewritingForRenamedClasses
- && lens.lookupType(clazz.type, appView.dexItemFactory()) != clazz.type) {
- String renamedClassifier = synthesizer.toRenamedClassifier(clazz.type);
- if (renamedClassifier != null) {
- assert !kmClass.getName().equals(renamedClassifier);
- kmClass.setName(renamedClassifier);
- }
- }
-
- // Rewriting upward hierarchy.
- List<KmType> superTypes = kmClass.getSupertypes();
- superTypes.clear();
- for (DexType itfType : clazz.interfaces.values) {
- // TODO(b/129925954): Use GenericSignature.ClassSignature#superInterfaceSignatures
- KmType kmType = synthesizer.toRenamedKmType(itfType, null, null, getTypeParameters());
- if (kmType != null) {
- superTypes.add(kmType);
- }
- }
- assert clazz.superType != null;
- if (clazz.superType != appView.dexItemFactory().objectType) {
- // TODO(b/129925954): Use GenericSignature.ClassSignature#superClassSignature
- KmType kmTypeForSupertype =
- synthesizer.toRenamedKmType(clazz.superType, null, null, getTypeParameters());
- if (kmTypeForSupertype != null) {
- superTypes.add(kmTypeForSupertype);
- }
- } else if (clazz.isInterface()) {
- superTypes.add(toKmType(addKotlinPrefix("Any;")));
- }
-
- // Rewriting downward hierarchies: nested, including companion class.
- // Note that `kotlinc` uses these nested classes to determine which classes to look up when
- // resolving declarations in the companion object, e.g., Host.Companion.prop and Host.prop.
- // Thus, users (in particular, library developers) should keep InnerClasses and EnclosingMethod
- // attributes if declarations in the companion need to be exposed.
- List<String> nestedClasses = kmClass.getNestedClasses();
- nestedClasses.clear();
- for (InnerClassAttribute innerClassAttribute : clazz.getInnerClasses()) {
- // Skip InnerClass attribute for itself.
- // Otherwise, an inner class would have itself as a nested class.
- if (clazz.getInnerClassAttributeForThisClass() == innerClassAttribute) {
- continue;
- }
- DexString renamedInnerName = lens.lookupInnerName(innerClassAttribute, appView.options());
- if (renamedInnerName != null) {
- nestedClasses.add(renamedInnerName.toString());
- }
- }
-
- // Rewriting downward hierarchies: sealed.
- List<String> sealedSubclasses = kmClass.getSealedSubclasses();
- sealedSubclasses.clear();
- if (IS_SEALED.invoke(kmClass.getFlags())) {
- for (DexType subtype : subtypingInfo.allImmediateSubtypes(clazz.type)) {
- String classifier = synthesizer.toRenamedClassifier(subtype);
- if (classifier != null) {
- sealedSubclasses.add(classifier);
- }
- }
- }
-
- if (!appView.options().enableKotlinMetadataRewritingForMembers) {
- return;
- }
-
- // Rewriting constructors.
- List<KmConstructor> constructors = kmClass.getConstructors();
- constructors.clear();
- for (DexEncodedMethod method : clazz.directMethods()) {
- if (!method.isInstanceInitializer()) {
- continue;
- }
- KmConstructor constructor = synthesizer.toRenamedKmConstructor(clazz, method);
- if (constructor != null) {
- constructors.add(constructor);
- }
- }
-
- // Rewriting companion object if any.
- if (kmClass.getCompanionObject() != null && hasCompanionObject()) {
- kmClass.setCompanionObject(lens.lookupName(companionObject).toString());
- }
-
- // TODO(b/151193864): enum entries
-
- rewriteDeclarationContainer(synthesizer);
- }
-
- @Override
- KotlinClassHeader createHeader() {
- KotlinClassMetadata.Class.Writer writer = new KotlinClassMetadata.Class.Writer();
- kmClass.accept(writer);
- return writer.write().getHeader();
- }
-
- @Override
- public Kind getKind() {
- return Kind.Class;
- }
-
- @Override
- public boolean isClass() {
- return true;
- }
-
- @Override
- public KotlinClass asClass() {
- return this;
- }
-
- @Override
- public String toString(String indent) {
- StringBuilder sb = new StringBuilder(indent);
- appendKmSection(
- indent,
- "Metadata.Class",
- sb,
- newIndent -> {
- appendKmClass(newIndent, sb, kmClass);
- });
- return sb.toString();
- }
-}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java
deleted file mode 100644
index 2a60521..0000000
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassFacade.java
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright (c) 2018, 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.kotlin;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.SubtypingInfo;
-import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.StringUtils;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.ListIterator;
-import kotlinx.metadata.jvm.KotlinClassHeader;
-import kotlinx.metadata.jvm.KotlinClassMetadata;
-
-public final class KotlinClassFacade extends KotlinInfo<KotlinClassMetadata.MultiFileClassFacade> {
-
- // TODO(b/151194869): is it better to maintain List<DexType>?
- List<String> partClassNames;
-
- static KotlinClassFacade fromKotlinClassMetadata(
- KotlinClassMetadata kotlinClassMetadata, DexClass clazz) {
- assert kotlinClassMetadata instanceof KotlinClassMetadata.MultiFileClassFacade;
- KotlinClassMetadata.MultiFileClassFacade multiFileClassFacade =
- (KotlinClassMetadata.MultiFileClassFacade) kotlinClassMetadata;
- return new KotlinClassFacade(multiFileClassFacade, clazz);
- }
-
- private KotlinClassFacade(KotlinClassMetadata.MultiFileClassFacade metadata, DexClass clazz) {
- super(metadata, clazz);
- }
-
- @Override
- void processMetadata(KotlinClassMetadata.MultiFileClassFacade metadata) {
- // Part Class names are stored in `d1`, which is immutable. Make a copy instead.
- partClassNames = new ArrayList<>(metadata.getPartClassNames());
- // No API to explore metadata details, hence nothing further to do.
- }
-
- @Override
- void rewrite(AppView<AppInfoWithLiveness> appView, SubtypingInfo subtypingInfo, NamingLens lens) {
- ListIterator<String> partClassIterator = partClassNames.listIterator();
- KotlinMetadataSynthesizer synthesizer = new KotlinMetadataSynthesizer(appView, lens, this);
- while (partClassIterator.hasNext()) {
- String partClassName = partClassIterator.next();
- partClassIterator.remove();
- DexType partClassType = appView.dexItemFactory().createType(
- DescriptorUtils.getDescriptorFromClassBinaryName(partClassName));
- String renamedPartClassName = synthesizer.toRenamedBinaryName(partClassType);
- if (renamedPartClassName != null) {
- partClassIterator.add(renamedPartClassName);
- }
- }
- }
-
- @Override
- KotlinClassHeader createHeader() {
- KotlinClassMetadata.MultiFileClassFacade.Writer writer =
- new KotlinClassMetadata.MultiFileClassFacade.Writer();
- return writer.write(partClassNames).getHeader();
- }
-
- @Override
- public Kind getKind() {
- return Kind.Facade;
- }
-
- @Override
- public boolean isClassFacade() {
- return true;
- }
-
- @Override
- public KotlinClassFacade asClassFacade() {
- return this;
- }
-
- @Override
- public String toString(String indent) {
- return indent + "MetaData.MultiFileClassFacade(" + StringUtils.join(partClassNames, ", ") + ")";
- }
-}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
new file mode 100644
index 0000000..b2f71cb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
@@ -0,0 +1,271 @@
+// 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.kotlin;
+
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toJvmFieldSignature;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toJvmMethodSignature;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import kotlinx.metadata.KmClass;
+import kotlinx.metadata.KmConstructor;
+import kotlinx.metadata.KmType;
+import kotlinx.metadata.jvm.JvmExtensionsKt;
+import kotlinx.metadata.jvm.JvmMethodSignature;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+
+public class KotlinClassInfo implements KotlinClassLevelInfo {
+
+ private final int flags;
+ private final String name;
+ private final String moduleName;
+ private final List<KotlinConstructorInfo> constructorsWithNoBacking;
+ private final KotlinDeclarationContainerInfo declarationContainerInfo;
+ private final List<KotlinTypeParameterInfo> typeParameters;
+ private final List<KotlinTypeInfo> superTypes;
+ private final List<DexType> sealedSubClasses;
+ private final List<DexType> nestedClasses;
+ // TODO(b/154347404): Understand enum entries.
+ private final List<String> enumEntries;
+ private final DexType anonymousObjectOrigin;
+
+ public KotlinClassInfo(
+ int flags,
+ String name,
+ String moduleName,
+ KotlinDeclarationContainerInfo declarationContainerInfo,
+ List<KotlinTypeParameterInfo> typeParameters,
+ List<KotlinConstructorInfo> constructorsWithNoBacking,
+ List<KotlinTypeInfo> superTypes,
+ List<DexType> sealedSubClasses,
+ List<DexType> nestedClasses,
+ List<String> enumEntries,
+ DexType anonymousObjectOrigin) {
+ this.flags = flags;
+ this.name = name;
+ this.moduleName = moduleName;
+ this.declarationContainerInfo = declarationContainerInfo;
+ this.typeParameters = typeParameters;
+ this.constructorsWithNoBacking = constructorsWithNoBacking;
+ this.superTypes = superTypes;
+ this.sealedSubClasses = sealedSubClasses;
+ this.nestedClasses = nestedClasses;
+ this.enumEntries = enumEntries;
+ this.anonymousObjectOrigin = anonymousObjectOrigin;
+ }
+
+ public static KotlinClassInfo create(KmClass kmClass, DexClass hostClass, AppView<?> appView) {
+ Map<String, DexEncodedField> fieldMap = new HashMap<>();
+ for (DexEncodedField field : hostClass.fields()) {
+ fieldMap.put(toJvmFieldSignature(field.field).asString(), field);
+ }
+ Map<String, DexEncodedMethod> methodMap = new HashMap<>();
+ for (DexEncodedMethod method : hostClass.methods()) {
+ methodMap.put(toJvmMethodSignature(method.method).asString(), method);
+ }
+ ImmutableList.Builder<KotlinConstructorInfo> notBackedConstructors = ImmutableList.builder();
+ for (KmConstructor kmConstructor : kmClass.getConstructors()) {
+ KotlinConstructorInfo constructorInfo = KotlinConstructorInfo.create(kmConstructor, appView);
+ JvmMethodSignature signature = JvmExtensionsKt.getSignature(kmConstructor);
+ if (signature != null) {
+ DexEncodedMethod method = methodMap.get(signature.asString());
+ if (method != null) {
+ method.setKotlinMemberInfo(constructorInfo);
+ continue;
+ }
+ }
+ // We could not find a definition for the constructor - add it to ensure the same output.
+ notBackedConstructors.add(constructorInfo);
+ }
+ KotlinDeclarationContainerInfo container =
+ KotlinDeclarationContainerInfo.create(kmClass, methodMap, fieldMap, appView);
+ setCompanionObject(kmClass, hostClass, appView);
+ return new KotlinClassInfo(
+ kmClass.getFlags(),
+ kmClass.name,
+ JvmExtensionsKt.getModuleName(kmClass),
+ container,
+ KotlinTypeParameterInfo.create(kmClass.getTypeParameters(), appView),
+ notBackedConstructors.build(),
+ getSuperTypes(kmClass.getSupertypes(), appView),
+ getSealedSubClasses(hostClass, kmClass.getSealedSubclasses(), appView),
+ getNestedClasses(hostClass, kmClass.getNestedClasses(), appView),
+ kmClass.getEnumEntries(),
+ getAnonymousObjectOrigin(kmClass, appView));
+ }
+
+ private static DexType getAnonymousObjectOrigin(KmClass kmClass, AppView<?> appView) {
+ String anonymousObjectOriginName = JvmExtensionsKt.getAnonymousObjectOriginName(kmClass);
+ if (anonymousObjectOriginName != null) {
+ return appView
+ .dexItemFactory()
+ .createType(DescriptorUtils.getDescriptorFromClassBinaryName(anonymousObjectOriginName));
+ }
+ return null;
+ }
+
+ private static List<DexType> getNestedClasses(
+ DexClass clazz, List<String> nestedClasses, AppView<?> appView) {
+ ImmutableList.Builder<DexType> nestedTypes = ImmutableList.builder();
+ for (String nestedClass : nestedClasses) {
+ String binaryName =
+ clazz.type.toBinaryName() + DescriptorUtils.INNER_CLASS_SEPARATOR + nestedClass;
+ DexType nestedType =
+ appView
+ .dexItemFactory()
+ .createType(DescriptorUtils.getDescriptorFromClassBinaryName(binaryName));
+ nestedTypes.add(nestedType);
+ }
+ return nestedTypes.build();
+ }
+
+ private static List<DexType> getSealedSubClasses(
+ DexClass clazz, List<String> sealedSubclasses, AppView<?> appView) {
+ ImmutableList.Builder<DexType> sealedTypes = ImmutableList.builder();
+ for (String sealedSubClass : sealedSubclasses) {
+ String binaryName =
+ sealedSubClass.replace(
+ DescriptorUtils.JAVA_PACKAGE_SEPARATOR, DescriptorUtils.INNER_CLASS_SEPARATOR);
+ DexType sealedType =
+ appView
+ .dexItemFactory()
+ .createType(DescriptorUtils.getDescriptorFromClassBinaryName(binaryName));
+ sealedTypes.add(sealedType);
+ }
+ return sealedTypes.build();
+ }
+
+ private static List<KotlinTypeInfo> getSuperTypes(List<KmType> superTypes, AppView<?> appView) {
+ ImmutableList.Builder<KotlinTypeInfo> superTypeInfos = ImmutableList.builder();
+ for (KmType superType : superTypes) {
+ superTypeInfos.add(KotlinTypeInfo.create(superType, appView));
+ }
+ return superTypeInfos.build();
+ }
+
+ private static void setCompanionObject(KmClass kmClass, DexClass hostClass, AppView<?> appView) {
+ String companionObjectName = kmClass.getCompanionObject();
+ if (companionObjectName == null) {
+ return;
+ }
+ for (DexEncodedField field : hostClass.fields()) {
+ if (field.field.name.toString().equals(companionObjectName)) {
+ field.setKotlinMemberInfo(new KotlinCompanionInfo());
+ return;
+ }
+ }
+ appView
+ .options()
+ .reporter
+ .warning(KotlinMetadataDiagnostic.missingCompanionObject(hostClass, companionObjectName));
+ }
+
+ @Override
+ public boolean isClass() {
+ return true;
+ }
+
+ @Override
+ public KotlinClassInfo asClass() {
+ return this;
+ }
+
+ @Override
+ public KotlinClassHeader rewrite(
+ DexClass clazz, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+ KmClass kmClass = new KmClass();
+ // TODO(b/154348683): Set flags.
+ kmClass.setFlags(flags);
+ // Set potentially renamed class name.
+ DexString originalDescriptor = clazz.type.descriptor;
+ DexString rewrittenDescriptor = namingLens.lookupDescriptor(clazz.type);
+ // If the original descriptor equals the rewritten descriptor, we pick the original name
+ // to preserve potential errors in the original name. As an example, the kotlin stdlib has
+ // name: .kotlin/collections/CollectionsKt___CollectionsKt$groupingBy$1, which seems incorrect.
+ kmClass.setName(
+ originalDescriptor.equals(rewrittenDescriptor)
+ ? this.name
+ : KotlinMetadataUtils.kotlinNameFromDescriptor(rewrittenDescriptor));
+ // Find a companion object.
+ for (DexEncodedField field : clazz.fields()) {
+ if (field.getKotlinMemberInfo().isCompanion()) {
+ field.getKotlinMemberInfo().asCompanion().rewrite(kmClass, field.field, namingLens);
+ }
+ }
+ // Take all not backed constructors because we will never find them in definitions.
+ for (KotlinConstructorInfo constructorInfo : constructorsWithNoBacking) {
+ constructorInfo.rewrite(kmClass, null, appView, namingLens);
+ }
+ // Find all constructors.
+ for (DexEncodedMethod method : clazz.methods()) {
+ if (method.getKotlinMemberInfo().isConstructor()) {
+ KotlinConstructorInfo constructorInfo = method.getKotlinMemberInfo().asConstructor();
+ constructorInfo.rewrite(kmClass, method, appView, namingLens);
+ }
+ }
+ // Rewrite functions, type-aliases and type-parameters.
+ declarationContainerInfo.rewrite(
+ kmClass::visitFunction,
+ kmClass::visitProperty,
+ kmClass::visitTypeAlias,
+ clazz,
+ appView,
+ namingLens);
+ // Rewrite type parameters.
+ for (KotlinTypeParameterInfo typeParameter : typeParameters) {
+ typeParameter.rewrite(kmClass::visitTypeParameter, appView, namingLens);
+ }
+ // Rewrite super types.
+ for (KotlinTypeInfo superType : superTypes) {
+ superType.rewrite(kmClass::visitSupertype, appView, namingLens);
+ }
+ // Rewrite nested classes.
+ for (DexType nestedClass : nestedClasses) {
+ if (appView.definitionFor(nestedClass) != null) {
+ String descriptor =
+ KotlinMetadataUtils.kotlinNameFromDescriptor(namingLens.lookupDescriptor(nestedClass));
+ // If the class is a nested class, it should be on the form Foo.Bar$Baz, where Baz is the
+ // name we should record.
+ int innerClassIndex = descriptor.lastIndexOf(DescriptorUtils.INNER_CLASS_SEPARATOR);
+ kmClass.visitNestedClass(descriptor.substring(innerClassIndex + 1));
+ }
+ }
+ // Rewrite sealed sub classes.
+ for (DexType sealedSubClass : sealedSubClasses) {
+ if (appView.definitionFor(sealedSubClass) != null) {
+ String descriptor =
+ KotlinMetadataUtils.kotlinNameFromDescriptor(
+ namingLens.lookupDescriptor(sealedSubClass));
+ kmClass.visitSealedSubclass(
+ descriptor.replace(
+ DescriptorUtils.INNER_CLASS_SEPARATOR, DescriptorUtils.JAVA_PACKAGE_SEPARATOR));
+ }
+ }
+ // TODO(b/154347404): Understand enum entries.
+ kmClass.getEnumEntries().addAll(enumEntries);
+
+ JvmExtensionsKt.setModuleName(kmClass, moduleName);
+ if (anonymousObjectOrigin != null) {
+ JvmExtensionsKt.setAnonymousObjectOriginName(
+ kmClass, KotlinMetadataUtils.kotlinNameFromDescriptor(anonymousObjectOrigin.descriptor));
+ }
+
+ KotlinClassMetadata.Class.Writer writer = new KotlinClassMetadata.Class.Writer();
+ kmClass.accept(writer);
+ return writer.write().getHeader();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassLevelInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassLevelInfo.java
new file mode 100644
index 0000000..76bfb00
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassLevelInfo.java
@@ -0,0 +1,57 @@
+// 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.kotlin;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+
+public interface KotlinClassLevelInfo {
+
+ default boolean isClass() {
+ return false;
+ }
+
+ default KotlinClassInfo asClass() {
+ return null;
+ }
+
+ default boolean isFileFacade() {
+ return false;
+ }
+
+ default KotlinFileFacadeInfo asFileFacade() {
+ return null;
+ }
+
+ default boolean isMultiFileFacade() {
+ return false;
+ }
+
+ default KotlinMultiFileClassFacadeInfo asMultiFileFacade() {
+ return null;
+ }
+
+ default boolean isMultiFileClassPart() {
+ return false;
+ }
+
+ default KotlinMultiFileClassPartInfo asMultiFileClassPart() {
+ return null;
+ }
+
+ default boolean isSyntheticClass() {
+ return false;
+ }
+
+ default KotlinSyntheticClassInfo asSyntheticClass() {
+ return null;
+ }
+
+ KotlinClassHeader rewrite(
+ DexClass clazz, AppView<AppInfoWithLiveness> appView, NamingLens namingLens);
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
index e49d99d..13ae5f5 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -1,9 +1,11 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// 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.kotlin;
-import com.android.tools.r8.DiagnosticsHandler;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
+
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationElement;
import com.android.tools.r8.graph.DexClass;
@@ -20,28 +22,38 @@
public final class KotlinClassMetadataReader {
- static KotlinInfo getKotlinInfo(
- Kotlin kotlin,
- DexClass clazz,
- DiagnosticsHandler reporter) {
+ public static KotlinClassLevelInfo getKotlinInfo(
+ Kotlin kotlin, DexClass clazz, AppView<?> appView) {
DexAnnotation meta = clazz.annotations().getFirstMatching(kotlin.metadata.kotlinMetadataType);
if (meta != null) {
try {
- return createKotlinInfo(kotlin, clazz, meta.annotation);
+ return createKotlinInfo(kotlin, clazz, meta.annotation, appView);
} catch (ClassCastException | InconsistentKotlinMetadataException | MetadataError e) {
- reporter.info(
- new StringDiagnostic("Class " + clazz.type.toSourceString()
- + " has malformed kotlin.Metadata: " + e.getMessage()));
+ appView
+ .options()
+ .reporter
+ .info(
+ new StringDiagnostic(
+ "Class "
+ + clazz.type.toSourceString()
+ + " has malformed kotlin.Metadata: "
+ + e.getMessage()));
} catch (Throwable e) {
- reporter.info(
- new StringDiagnostic("Unexpected error while reading " + clazz.type.toSourceString()
- + "'s kotlin.Metadata: " + e.getMessage()));
+ appView
+ .options()
+ .reporter
+ .info(
+ new StringDiagnostic(
+ "Unexpected error while reading "
+ + clazz.type.toSourceString()
+ + "'s kotlin.Metadata: "
+ + e.getMessage()));
}
}
- return null;
+ return NO_KOTLIN_INFO;
}
- private static KotlinClassMetadata toKotlinClassMetadata(
+ public static KotlinClassMetadata toKotlinClassMetadata(
Kotlin kotlin, DexEncodedAnnotation metadataAnnotation) {
Map<DexString, DexAnnotationElement> elementMap = new IdentityHashMap<>();
for (DexAnnotationElement element : metadataAnnotation.elements) {
@@ -72,23 +84,28 @@
return KotlinClassMetadata.read(header);
}
- public static KotlinInfo createKotlinInfo(
- Kotlin kotlin, DexClass clazz, DexEncodedAnnotation metadataAnnotation) {
+ public static KotlinClassLevelInfo createKotlinInfo(
+ Kotlin kotlin, DexClass clazz, DexEncodedAnnotation metadataAnnotation, AppView<?> appView) {
KotlinClassMetadata kMetadata = toKotlinClassMetadata(kotlin, metadataAnnotation);
if (kMetadata instanceof KotlinClassMetadata.Class) {
- return KotlinClass.fromKotlinClassMetadata(kMetadata, clazz);
+ return KotlinClassInfo.create(
+ ((KotlinClassMetadata.Class) kMetadata).toKmClass(), clazz, appView);
} else if (kMetadata instanceof KotlinClassMetadata.FileFacade) {
// e.g., B.kt becomes class `BKt`
- return KotlinFile.fromKotlinClassMetadata(kMetadata, clazz);
+ return KotlinFileFacadeInfo.create(
+ (KotlinClassMetadata.FileFacade) kMetadata, clazz, appView);
} else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassFacade) {
// multi-file class with the same @JvmName.
- return KotlinClassFacade.fromKotlinClassMetadata(kMetadata, clazz);
+ return KotlinMultiFileClassFacadeInfo.create(
+ (KotlinClassMetadata.MultiFileClassFacade) kMetadata, appView);
} else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassPart) {
// A single file, which is part of multi-file class.
- return KotlinClassPart.fromKotlinClassMetadata(kMetadata, clazz);
+ return KotlinMultiFileClassPartInfo.create(
+ (KotlinClassMetadata.MultiFileClassPart) kMetadata, clazz, appView);
} else if (kMetadata instanceof KotlinClassMetadata.SyntheticClass) {
- return KotlinSyntheticClass.fromKotlinClassMetadata(kMetadata, kotlin, clazz);
+ return KotlinSyntheticClassInfo.create(
+ (KotlinClassMetadata.SyntheticClass) kMetadata, clazz, kotlin, appView);
} else {
throw new MetadataError("unsupported 'k' value: " + kMetadata.getHeader().getKind());
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
deleted file mode 100644
index f0d15a4..0000000
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassPart.java
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright (c) 2018, 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.kotlin;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.SubtypingInfo;
-import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.DescriptorUtils;
-import kotlinx.metadata.KmPackage;
-import kotlinx.metadata.jvm.KotlinClassHeader;
-import kotlinx.metadata.jvm.KotlinClassMetadata;
-
-public final class KotlinClassPart extends KotlinInfo<KotlinClassMetadata.MultiFileClassPart> {
-
- KmPackage kmPackage;
- // TODO(b/151194869): is it better to maintain DexType?
- String facadeClassName;
-
- static KotlinClassPart fromKotlinClassMetadata(
- KotlinClassMetadata kotlinClassMetadata, DexClass clazz) {
- assert kotlinClassMetadata instanceof KotlinClassMetadata.MultiFileClassPart;
- KotlinClassMetadata.MultiFileClassPart multiFileClassPart =
- (KotlinClassMetadata.MultiFileClassPart) kotlinClassMetadata;
- return new KotlinClassPart(multiFileClassPart, clazz);
- }
-
- private KotlinClassPart(KotlinClassMetadata.MultiFileClassPart metadata, DexClass clazz) {
- super(metadata, clazz);
- }
-
- @Override
- void processMetadata(KotlinClassMetadata.MultiFileClassPart metadata) {
- kmPackage = metadata.toKmPackage();
- facadeClassName = metadata.getFacadeClassName();
- }
-
- @Override
- void rewrite(AppView<AppInfoWithLiveness> appView, SubtypingInfo subtypingInfo, NamingLens lens) {
- DexType facadeClassType = appView.dexItemFactory().createType(
- DescriptorUtils.getDescriptorFromClassBinaryName(facadeClassName));
- KotlinMetadataSynthesizer synthesizer = new KotlinMetadataSynthesizer(appView, lens, this);
- facadeClassName = synthesizer.toRenamedBinaryName(facadeClassType);
- if (!appView.options().enableKotlinMetadataRewritingForMembers) {
- return;
- }
- rewriteDeclarationContainer(synthesizer);
- }
-
- @Override
- KotlinClassHeader createHeader() {
- if (facadeClassName != null) {
- KotlinClassMetadata.MultiFileClassPart.Writer writer =
- new KotlinClassMetadata.MultiFileClassPart.Writer();
- kmPackage.accept(writer);
- return writer.write(facadeClassName).getHeader();
- } else {
- // It's no longer part of multi-file class.
- KotlinClassMetadata.FileFacade.Writer writer = new KotlinClassMetadata.FileFacade.Writer();
- kmPackage.accept(writer);
- return writer.write().getHeader();
- }
- }
-
- @Override
- public Kind getKind() {
- return Kind.Part;
- }
-
- @Override
- public boolean isClassPart() {
- return true;
- }
-
- @Override
- public KotlinClassPart asClassPart() {
- return this;
- }
-
- @Override
- public String toString(String indent) {
- StringBuilder sb = new StringBuilder(indent);
- appendKmSection(
- indent,
- "Metadata.MultiFileClassPart",
- sb,
- newIndent -> {
- appendKeyValue(newIndent, "facadeClassName", sb, facadeClassName);
- appendKmPackage(newIndent, sb, kmPackage);
- });
- return sb.toString();
- }
-}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java
index c7c3701..86db718 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassifierInfo.java
@@ -4,46 +4,129 @@
package com.android.tools.r8.kotlin;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.kotlin.Kotlin.ClassClassifiers;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.DescriptorUtils;
import kotlinx.metadata.KmClassifier;
+import kotlinx.metadata.KmClassifier.TypeAlias;
+import kotlinx.metadata.KmClassifier.TypeParameter;
+import kotlinx.metadata.KmTypeVisitor;
-// Provides access to information about a kotlin classifier
-public class KotlinClassifierInfo {
+public abstract class KotlinClassifierInfo {
- private static boolean isClass(KmClassifier classifier) {
- return classifier instanceof KmClassifier.Class;
- }
-
- private static KmClassifier.Class getClassClassifier(KmClassifier classifier) {
- return (KmClassifier.Class) classifier;
- }
-
- private static boolean isTypeAlias(KmClassifier classifier) {
- return classifier instanceof KmClassifier.TypeAlias;
- }
-
- private static KmClassifier.TypeAlias getTypeAlias(KmClassifier classifier) {
- return (KmClassifier.TypeAlias) classifier;
- }
-
- private static boolean isTypeParameter(KmClassifier classifier) {
- return classifier instanceof KmClassifier.TypeParameter;
- }
-
- private static KmClassifier.TypeParameter getTypeParameter(KmClassifier classifier) {
- return (KmClassifier.TypeParameter) classifier;
- }
-
- public static boolean equals(KmClassifier one, KmClassifier other) {
- if (isClass(one)) {
- return isClass(other)
- && getClassClassifier(one).getName().equals(getClassClassifier(other).getName());
+ public static KotlinClassifierInfo create(KmClassifier classifier, AppView<?> appView) {
+ if (classifier instanceof KmClassifier.Class) {
+ String typeName = ((KmClassifier.Class) classifier).getName();
+ // If this name starts with '.', it represents a local class or an anonymous object. This is
+ // used by the Kotlin compiler to prevent lookup of this name in the resolution:
+ // .kotlin/random/FallbackThreadLocalRandom$implStorage$1
+ if (typeName.startsWith(".")) {
+ return new KotlinUnknownClassClassifierInfo(typeName);
+ }
+ String descriptor = DescriptorUtils.getDescriptorFromKotlinClassifier(typeName);
+ if (DescriptorUtils.isClassDescriptor(descriptor)) {
+ DexType type = appView.dexItemFactory().createType(descriptor);
+ return new KotlinClassClassifierInfo(type);
+ } else {
+ return new KotlinUnknownClassClassifierInfo(typeName);
+ }
+ } else if (classifier instanceof KmClassifier.TypeAlias) {
+ return new KotlinTypeAliasClassifierInfo(((TypeAlias) classifier).getName());
+ } else if (classifier instanceof KmClassifier.TypeParameter) {
+ return new KotlinTypeParameterClassifierInfo(((TypeParameter) classifier).getId());
+ } else {
+ appView
+ .options()
+ .reporter
+ .warning(KotlinMetadataDiagnostic.unknownClassifier(classifier.toString()));
+ return new KotlinUnknownClassifierInfo(classifier.toString());
}
- if (isTypeAlias(one)) {
- return isTypeAlias(other)
- && getTypeAlias(one).getName().equals(getTypeAlias(other).getName());
+ }
+
+ abstract void rewrite(
+ KmTypeVisitor visitor, AppView<AppInfoWithLiveness> appView, NamingLens namingLens);
+
+ public static class KotlinClassClassifierInfo extends KotlinClassifierInfo {
+
+ private final DexType type;
+
+ private KotlinClassClassifierInfo(DexType type) {
+ this.type = type;
}
- assert isTypeParameter(one);
- return isTypeParameter(other)
- && getTypeParameter(one).getId() == getTypeParameter(other).getId();
+
+ @Override
+ void rewrite(
+ KmTypeVisitor visitor, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+ String classifier;
+ if (appView.appInfo().wasPruned(type)) {
+ classifier = ClassClassifiers.anyName;
+ } else {
+ DexString descriptor = namingLens.lookupDescriptor(type);
+ classifier = DescriptorUtils.descriptorToKotlinClassifier(descriptor.toString());
+ }
+ visitor.visitClass(classifier);
+ }
+ }
+
+ public static class KotlinTypeParameterClassifierInfo extends KotlinClassifierInfo {
+
+ private final int typeId;
+
+ private KotlinTypeParameterClassifierInfo(int typeId) {
+ this.typeId = typeId;
+ }
+
+ @Override
+ void rewrite(
+ KmTypeVisitor visitor, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+ visitor.visitTypeParameter(typeId);
+ }
+ }
+
+ public static class KotlinTypeAliasClassifierInfo extends KotlinClassifierInfo {
+
+ private final String typeAlias;
+
+ private KotlinTypeAliasClassifierInfo(String typeAlias) {
+ this.typeAlias = typeAlias;
+ }
+
+ @Override
+ void rewrite(
+ KmTypeVisitor visitor, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+ visitor.visitTypeAlias(typeAlias);
+ }
+ }
+
+ public static class KotlinUnknownClassClassifierInfo extends KotlinClassifierInfo {
+ private final String classifier;
+
+ private KotlinUnknownClassClassifierInfo(String classifier) {
+ this.classifier = classifier;
+ }
+
+ @Override
+ void rewrite(
+ KmTypeVisitor visitor, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+ visitor.visitClass(classifier);
+ }
+ }
+
+ public static class KotlinUnknownClassifierInfo extends KotlinClassifierInfo {
+ private final String classifier;
+
+ private KotlinUnknownClassifierInfo(String classifier) {
+ this.classifier = classifier;
+ }
+
+ @Override
+ void rewrite(
+ KmTypeVisitor visitor, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+ visitor.visitTypeAlias(classifier);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinCompanionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinCompanionInfo.java
new file mode 100644
index 0000000..2f97b4f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinCompanionInfo.java
@@ -0,0 +1,30 @@
+// 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.kotlin;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.naming.NamingLens;
+import kotlinx.metadata.KmClassVisitor;
+
+// Structure around a kotlin companion object that can be assigned to a field.
+public class KotlinCompanionInfo implements KotlinFieldLevelInfo {
+
+ @Override
+ public boolean isCompanion() {
+ return true;
+ }
+
+ @Override
+ public KotlinCompanionInfo asCompanion() {
+ return this;
+ }
+
+ public void rewrite(KmClassVisitor visitor, DexField field, NamingLens lens) {
+ DexString dexString = lens.lookupName(field);
+ String finalName = dexString.toString();
+ visitor.visitCompanionObject(finalName);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
new file mode 100644
index 0000000..3e9ee04
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinConstructorInfo.java
@@ -0,0 +1,69 @@
+// 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.kotlin;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.List;
+import kotlinx.metadata.KmClass;
+import kotlinx.metadata.KmConstructor;
+import kotlinx.metadata.jvm.JvmExtensionsKt;
+
+// Holds information about a KmConstructor object.
+public class KotlinConstructorInfo implements KotlinMethodLevelInfo {
+
+ // Information from original KmValueParameter(s) if available.
+ private final int flags;
+ // Information about the value parameters.
+ private final List<KotlinValueParameterInfo> valueParameterInfos;
+ // Information about the signature.
+ private final KotlinJvmMethodSignatureInfo signature;
+
+ private KotlinConstructorInfo(
+ int flags,
+ List<KotlinValueParameterInfo> valueParameterInfos,
+ KotlinJvmMethodSignatureInfo signature) {
+ this.flags = flags;
+ this.valueParameterInfos = valueParameterInfos;
+ this.signature = signature;
+ }
+
+ public static KotlinConstructorInfo create(KmConstructor kmConstructor, AppView<?> appView) {
+ return new KotlinConstructorInfo(
+ kmConstructor.getFlags(),
+ KotlinValueParameterInfo.create(kmConstructor.getValueParameters(), appView),
+ KotlinJvmMethodSignatureInfo.create(JvmExtensionsKt.getSignature(kmConstructor), appView));
+ }
+
+ public void rewrite(
+ KmClass kmClass,
+ DexEncodedMethod method,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens namingLens) {
+ // Note that JvmExtensionsKt.setSignature does not have an overload for KmConstructorVisitor,
+ // thus we rely on creating the KmConstructor manually.
+ // TODO(b/154348683): Check for special flags to pass in.
+ KmConstructor kmConstructor = new KmConstructor(flags);
+ if (signature != null) {
+ JvmExtensionsKt.setSignature(kmConstructor, signature.rewrite(method, appView, namingLens));
+ }
+ for (KotlinValueParameterInfo valueParameterInfo : valueParameterInfos) {
+ valueParameterInfo.rewrite(kmConstructor::visitValueParameter, appView, namingLens);
+ }
+ kmClass.getConstructors().add(kmConstructor);
+ }
+
+ @Override
+ public boolean isConstructor() {
+ return true;
+ }
+
+ @Override
+ public KotlinConstructorInfo asConstructor() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
new file mode 100644
index 0000000..d1b2913
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
@@ -0,0 +1,207 @@
+// 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.kotlin;
+
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.isValidMethodDescriptor;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.kotlin.KotlinMetadataUtils.KmPropertyProcessor;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.ImmutableList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import kotlinx.metadata.KmDeclarationContainer;
+import kotlinx.metadata.KmFunction;
+import kotlinx.metadata.KmProperty;
+import kotlinx.metadata.KmTypeAlias;
+import kotlinx.metadata.jvm.JvmExtensionsKt;
+import kotlinx.metadata.jvm.JvmMethodSignature;
+
+// Holds information about KmDeclarationContainer
+public class KotlinDeclarationContainerInfo {
+
+ private final List<KotlinTypeAliasInfo> typeAliases;
+ // The functions in notBackedFunctions are KmFunctions where we could not find a representative.
+ private final List<KotlinFunctionInfo> functionsWithNoBacking;
+ // The properties in propertiesWithNoBacking are KmProperties where we could not find a getter,
+ // setter or backing field.
+ private final List<KotlinPropertyInfo> propertiesWithNoBacking;
+
+ private KotlinDeclarationContainerInfo(
+ List<KotlinTypeAliasInfo> typeAliases,
+ List<KotlinFunctionInfo> functionsWithNoBacking,
+ List<KotlinPropertyInfo> propertiesWithNoBacking) {
+ this.typeAliases = typeAliases;
+ this.functionsWithNoBacking = functionsWithNoBacking;
+ this.propertiesWithNoBacking = propertiesWithNoBacking;
+ }
+
+ public static KotlinDeclarationContainerInfo create(
+ KmDeclarationContainer container,
+ Map<String, DexEncodedMethod> methodSignatureMap,
+ Map<String, DexEncodedField> fieldSignatureMap,
+ AppView<?> appView) {
+ ImmutableList.Builder<KotlinFunctionInfo> notBackedFunctions = ImmutableList.builder();
+ for (KmFunction kmFunction : container.getFunctions()) {
+ JvmMethodSignature signature = JvmExtensionsKt.getSignature(kmFunction);
+ if (signature == null) {
+ assert false;
+ continue;
+ }
+ KotlinFunctionInfo kotlinFunctionInfo = KotlinFunctionInfo.create(kmFunction, appView);
+ DexEncodedMethod method = methodSignatureMap.get(signature.asString());
+ if (method == null) {
+ notBackedFunctions.add(kotlinFunctionInfo);
+ if (!isValidMethodDescriptor(signature.getDesc())) {
+ // TODO(b/155536535): Enable this assert.
+ // appView
+ // .options()
+ // .reporter
+ // .info(KotlinMetadataDiagnostic.invalidMethodDescriptor(signature.asString()));
+ } else {
+ // TODO(b/154348568): Enable the assertion below.
+ // assert false : "Could not find method with signature " + signature.asString();
+ }
+ continue;
+ }
+ method.setKotlinMemberInfo(kotlinFunctionInfo);
+ }
+
+ ImmutableList.Builder<KotlinPropertyInfo> notBackedProperties = ImmutableList.builder();
+ for (KmProperty kmProperty : container.getProperties()) {
+ KotlinPropertyInfo kotlinPropertyInfo = KotlinPropertyInfo.create(kmProperty, appView);
+ KmPropertyProcessor propertyProcessor = new KmPropertyProcessor(kmProperty);
+ boolean hasBacking = false;
+ if (propertyProcessor.fieldSignature() != null) {
+ DexEncodedField field =
+ fieldSignatureMap.get(propertyProcessor.fieldSignature().asString());
+ if (field != null) {
+ hasBacking = true;
+ field.setKotlinMemberInfo(kotlinPropertyInfo);
+ }
+ }
+ if (propertyProcessor.getterSignature() != null) {
+ DexEncodedMethod method =
+ methodSignatureMap.get(propertyProcessor.getterSignature().asString());
+ if (method != null) {
+ hasBacking = true;
+ method.setKotlinMemberInfo(kotlinPropertyInfo);
+ }
+ }
+ if (propertyProcessor.setterSignature() != null) {
+ DexEncodedMethod method =
+ methodSignatureMap.get(propertyProcessor.setterSignature().asString());
+ if (method != null) {
+ hasBacking = true;
+ method.setKotlinMemberInfo(kotlinPropertyInfo);
+ }
+ }
+ if (!hasBacking) {
+ notBackedProperties.add(kotlinPropertyInfo);
+ }
+ }
+ return new KotlinDeclarationContainerInfo(
+ getTypeAliases(container.getTypeAliases(), appView),
+ notBackedFunctions.build(),
+ notBackedProperties.build());
+ }
+
+ private static List<KotlinTypeAliasInfo> getTypeAliases(
+ List<KmTypeAlias> aliases, AppView<?> appView) {
+ ImmutableList.Builder<KotlinTypeAliasInfo> builder = ImmutableList.builder();
+ for (KmTypeAlias alias : aliases) {
+ builder.add(KotlinTypeAliasInfo.create(alias, appView));
+ }
+ return builder.build();
+ }
+
+ public void rewrite(
+ KmVisitorProviders.KmFunctionVisitorProvider functionProvider,
+ KmVisitorProviders.KmPropertyVisitorProvider propertyProvider,
+ KmVisitorProviders.KmTypeAliasVisitorProvider typeAliasProvider,
+ DexClass clazz,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens namingLens) {
+ // Type aliases only have a representation here, so we can generate them directly.
+ for (KotlinTypeAliasInfo typeAlias : typeAliases) {
+ typeAlias.rewrite(typeAliasProvider, appView, namingLens);
+ }
+ // For properties, we need to combine potentially a field, setter and getter.
+ Map<KotlinPropertyInfo, KotlinPropertyGroup> properties = new IdentityHashMap<>();
+ for (DexEncodedField field : clazz.fields()) {
+ if (field.getKotlinMemberInfo().isFieldProperty()) {
+ properties
+ .computeIfAbsent(
+ field.getKotlinMemberInfo().asFieldProperty(), ignored -> new KotlinPropertyGroup())
+ .setBackingField(field);
+ }
+ }
+ for (DexEncodedMethod method : clazz.methods()) {
+ if (method.getKotlinMemberInfo().isFunction()) {
+ method
+ .getKotlinMemberInfo()
+ .asFunction()
+ .rewrite(functionProvider, method, appView, namingLens);
+ continue;
+ }
+ KotlinPropertyInfo kotlinPropertyInfo = method.getKotlinMemberInfo().asProperty();
+ if (kotlinPropertyInfo == null) {
+ continue;
+ }
+ KotlinPropertyGroup kotlinPropertyGroup =
+ properties.computeIfAbsent(kotlinPropertyInfo, ignored -> new KotlinPropertyGroup());
+ if (method.method.proto.returnType == appView.dexItemFactory().voidType) {
+ // This is a setter.
+ kotlinPropertyGroup.setSetter(method);
+ } else {
+ kotlinPropertyGroup.setGetter(method);
+ }
+ }
+ for (KotlinPropertyInfo kotlinPropertyInfo : properties.keySet()) {
+ KotlinPropertyGroup kotlinPropertyGroup = properties.get(kotlinPropertyInfo);
+ kotlinPropertyInfo.rewrite(
+ propertyProvider,
+ kotlinPropertyGroup.backingField,
+ kotlinPropertyGroup.getter,
+ kotlinPropertyGroup.setter,
+ appView,
+ namingLens);
+ }
+ // Add all not backed functions and properties.
+ for (KotlinFunctionInfo notBackedFunction : functionsWithNoBacking) {
+ notBackedFunction.rewrite(functionProvider, null, appView, namingLens);
+ }
+ for (KotlinPropertyInfo notBackedProperty : propertiesWithNoBacking) {
+ notBackedProperty.rewrite(propertyProvider, null, null, null, appView, namingLens);
+ }
+ }
+
+ public static class KotlinPropertyGroup {
+
+ private DexEncodedField backingField = null;
+ private DexEncodedMethod setter = null;
+ private DexEncodedMethod getter = null;
+
+ void setBackingField(DexEncodedField backingField) {
+ assert this.backingField == null;
+ this.backingField = backingField;
+ }
+
+ void setGetter(DexEncodedMethod getter) {
+ assert this.getter == null;
+ this.getter = getter;
+ }
+
+ void setSetter(DexEncodedMethod setter) {
+ assert this.setter == null;
+ this.setter = setter;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFieldLevelInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFieldLevelInfo.java
new file mode 100644
index 0000000..7df7afb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFieldLevelInfo.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin;
+
+public interface KotlinFieldLevelInfo {
+
+ default boolean isCompanion() {
+ return false;
+ }
+
+ default KotlinCompanionInfo asCompanion() {
+ return null;
+ }
+
+ default boolean isFieldProperty() {
+ return false;
+ }
+
+ default KotlinPropertyInfo asFieldProperty() {
+ return null;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
deleted file mode 100644
index ae7eb41..0000000
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFile.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (c) 2018, 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.kotlin;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.SubtypingInfo;
-import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import kotlinx.metadata.KmPackage;
-import kotlinx.metadata.jvm.KotlinClassHeader;
-import kotlinx.metadata.jvm.KotlinClassMetadata;
-
-public final class KotlinFile extends KotlinInfo<KotlinClassMetadata.FileFacade> {
-
- KmPackage kmPackage;
-
- static KotlinFile fromKotlinClassMetadata(
- KotlinClassMetadata kotlinClassMetadata, DexClass clazz) {
- assert kotlinClassMetadata instanceof KotlinClassMetadata.FileFacade;
- KotlinClassMetadata.FileFacade fileFacade =
- (KotlinClassMetadata.FileFacade) kotlinClassMetadata;
- return new KotlinFile(fileFacade, clazz);
- }
-
- private KotlinFile(KotlinClassMetadata.FileFacade metadata, DexClass clazz) {
- super(metadata, clazz);
- }
-
- @Override
- void processMetadata(KotlinClassMetadata.FileFacade metadata) {
- kmPackage = metadata.toKmPackage();
- }
-
- @Override
- void rewrite(AppView<AppInfoWithLiveness> appView, SubtypingInfo subtypingInfo, NamingLens lens) {
- if (!appView.options().enableKotlinMetadataRewritingForMembers) {
- return;
- }
- rewriteDeclarationContainer(new KotlinMetadataSynthesizer(appView, lens, this));
- }
-
- @Override
- KotlinClassHeader createHeader() {
- KotlinClassMetadata.FileFacade.Writer writer = new KotlinClassMetadata.FileFacade.Writer();
- kmPackage.accept(writer);
- return writer.write().getHeader();
- }
-
- @Override
- public Kind getKind() {
- return Kind.File;
- }
-
- @Override
- public boolean isFile() {
- return true;
- }
-
- @Override
- public KotlinFile asFile() {
- return this;
- }
-
- @Override
- public String toString(String indent) {
- StringBuilder sb = new StringBuilder(indent);
- appendKmSection(
- indent,
- "Metadata.FileFacade",
- sb,
- newIndent -> {
- appendKmPackage(newIndent, sb, kmPackage);
- });
- return sb.toString();
- }
-}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
new file mode 100644
index 0000000..ee2f81d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import kotlinx.metadata.KmPackage;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+import kotlinx.metadata.jvm.KotlinClassMetadata.FileFacade;
+
+// Holds information about Metadata.FileFacade
+public class KotlinFileFacadeInfo implements KotlinClassLevelInfo {
+
+ private final KotlinPackageInfo packageInfo;
+
+ private KotlinFileFacadeInfo(KotlinPackageInfo packageInfo) {
+ this.packageInfo = packageInfo;
+ }
+
+ public static KotlinFileFacadeInfo create(
+ FileFacade kmFileFacade, DexClass clazz, AppView<?> appView) {
+ return new KotlinFileFacadeInfo(
+ KotlinPackageInfo.create(kmFileFacade.toKmPackage(), clazz, appView));
+ }
+
+ @Override
+ public boolean isFileFacade() {
+ return true;
+ }
+
+ @Override
+ public KotlinFileFacadeInfo asFileFacade() {
+ return this;
+ }
+
+ @Override
+ public KotlinClassHeader rewrite(
+ DexClass clazz, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+ KotlinClassMetadata.FileFacade.Writer writer = new KotlinClassMetadata.FileFacade.Writer();
+ KmPackage kmPackage = new KmPackage();
+ packageInfo.rewrite(kmPackage, clazz, appView, namingLens);
+ kmPackage.accept(writer);
+ return writer.write().getHeader();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
new file mode 100644
index 0000000..c1170a1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
@@ -0,0 +1,129 @@
+// 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.kotlin;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.util.List;
+import kotlinx.metadata.KmFunction;
+import kotlinx.metadata.KmFunctionVisitor;
+import kotlinx.metadata.jvm.JvmExtensionsKt;
+import kotlinx.metadata.jvm.JvmFunctionExtensionVisitor;
+
+// Holds information about KmFunction
+public final class KotlinFunctionInfo implements KotlinMethodLevelInfo {
+ // Original flags
+ private final int flags;
+ // Original name;
+ private final String name;
+ // Information from original KmValueParameter(s) if available.
+ private final List<KotlinValueParameterInfo> valueParameters;
+ // Information from original KmFunction.returnType. Null if this is from a KmConstructor.
+ public final KotlinTypeInfo returnType;
+ // Information from original KmFunction.receiverType. Null if this is from a KmConstructor.
+ private final KotlinTypeInfo receiverParameterType;
+ // Information about original type parameters. Null if this is from a KmConstructor.
+ private final List<KotlinTypeParameterInfo> typeParameters;
+ // Information about the signature
+ private final KotlinJvmMethodSignatureInfo signature;
+ // Information about the lambdaClassOrigin.
+ private final DexType lambdaClassOrigin;
+
+ private KotlinFunctionInfo(
+ int flags,
+ String name,
+ KotlinTypeInfo returnType,
+ KotlinTypeInfo receiverParameterType,
+ List<KotlinValueParameterInfo> valueParameters,
+ List<KotlinTypeParameterInfo> typeParameters,
+ KotlinJvmMethodSignatureInfo signature,
+ DexType lambdaClassOrigin) {
+ this.flags = flags;
+ this.name = name;
+ this.returnType = returnType;
+ this.receiverParameterType = receiverParameterType;
+ this.valueParameters = valueParameters;
+ this.typeParameters = typeParameters;
+ this.signature = signature;
+ this.lambdaClassOrigin = lambdaClassOrigin;
+ }
+
+ static KotlinFunctionInfo create(KmFunction kmFunction, AppView<?> appView) {
+ return new KotlinFunctionInfo(
+ kmFunction.getFlags(),
+ kmFunction.getName(),
+ KotlinTypeInfo.create(kmFunction.getReturnType(), appView),
+ KotlinTypeInfo.create(kmFunction.getReceiverParameterType(), appView),
+ KotlinValueParameterInfo.create(kmFunction.getValueParameters(), appView),
+ KotlinTypeParameterInfo.create(kmFunction.getTypeParameters(), appView),
+ KotlinJvmMethodSignatureInfo.create(JvmExtensionsKt.getSignature(kmFunction), appView),
+ getlambdaClassOrigin(kmFunction, appView));
+ }
+
+ private static DexType getlambdaClassOrigin(KmFunction kmFunction, AppView<?> appView) {
+ String lambdaClassOriginName = JvmExtensionsKt.getLambdaClassOriginName(kmFunction);
+ if (lambdaClassOriginName != null) {
+ return appView
+ .dexItemFactory()
+ .createType(DescriptorUtils.getDescriptorFromClassBinaryName(lambdaClassOriginName));
+ }
+ return null;
+ }
+
+ public void rewrite(
+ KmVisitorProviders.KmFunctionVisitorProvider visitorProvider,
+ DexEncodedMethod method,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens namingLens) {
+ // TODO(b/154348683): Check method for flags to pass in.
+ String finalName = this.name;
+ if (method != null) {
+ String methodName = method.method.name.toString();
+ String rewrittenName = namingLens.lookupName(method.method).toString();
+ if (!methodName.equals(rewrittenName)) {
+ finalName = rewrittenName;
+ }
+ }
+ KmFunctionVisitor kmFunction = visitorProvider.get(flags, finalName);
+ // TODO(b/154348149): ReturnType could have been merged to a subtype.
+ returnType.rewrite(kmFunction::visitReturnType, appView, namingLens);
+ for (KotlinValueParameterInfo valueParameterInfo : valueParameters) {
+ valueParameterInfo.rewrite(kmFunction::visitValueParameter, appView, namingLens);
+ }
+ for (KotlinTypeParameterInfo typeParameterInfo : typeParameters) {
+ typeParameterInfo.rewrite(kmFunction::visitTypeParameter, appView, namingLens);
+ }
+ if (receiverParameterType != null) {
+ receiverParameterType.rewrite(kmFunction::visitReceiverParameterType, appView, namingLens);
+ }
+ JvmFunctionExtensionVisitor extensionVisitor =
+ (JvmFunctionExtensionVisitor) kmFunction.visitExtensions(JvmFunctionExtensionVisitor.TYPE);
+ if (signature != null && extensionVisitor != null) {
+ extensionVisitor.visit(signature.rewrite(method, appView, namingLens));
+ }
+ if (lambdaClassOrigin != null && extensionVisitor != null) {
+ extensionVisitor.visitLambdaClassOriginName(
+ KotlinMetadataUtils.kotlinNameFromDescriptor(lambdaClassOrigin.descriptor));
+ }
+ }
+
+ @Override
+ public boolean isFunction() {
+ return true;
+ }
+
+ @Override
+ public KotlinFunctionInfo asFunction() {
+ return this;
+ }
+
+ public boolean isExtensionFunction() {
+ return receiverParameterType != null;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinInfoCollector.java b/src/main/java/com/android/tools/r8/kotlin/KotlinInfoCollector.java
index 9d89660..94f9b12 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinInfoCollector.java
@@ -5,12 +5,7 @@
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.graph.DexType;
-import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.ThreadUtils;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -22,40 +17,10 @@
return;
}
Kotlin kotlin = appView.dexItemFactory().kotlin;
- Reporter reporter = appView.options().reporter;
- Map<DexProgramClass, DexProgramClass> companionToHostMap = new ConcurrentHashMap<>();
ThreadUtils.processItems(
application.classes(),
programClass -> {
- KotlinInfo kotlinInfo = kotlin.getKotlinInfo(programClass, reporter);
- programClass.setKotlinInfo(kotlinInfo);
- KotlinMemberInfo.markKotlinMemberInfo(programClass, kotlinInfo, reporter);
- // Store a companion type to revisit.
- if (kotlinInfo != null
- && kotlinInfo.isClass()
- && kotlinInfo.asClass().hasCompanionObject()) {
- DexType companionType = kotlinInfo.asClass().getCompanionObjectType();
- DexProgramClass companionClass = appView.definitionForProgramType(companionType);
- if (companionClass != null) {
- companionToHostMap.put(companionClass, programClass);
- }
- }
- },
- executorService);
- // TODO(b/151194869): if we can guarantee that Companion classes are visited ahead and their
- // KotlinInfo is created before processing host classes, below could be hoisted to 1st pass.
- // Maybe name-based filtering? E.g., classes whose name ends with "$Companion" v.s. not?
- ThreadUtils.processItems(
- companionToHostMap.keySet(),
- companionClass -> {
- KotlinInfo kotlinInfo = companionClass.getKotlinInfo();
- if (kotlinInfo != null && kotlinInfo.isClass()) {
- DexProgramClass hostClass = companionToHostMap.get(companionClass);
- assert hostClass != null;
- kotlinInfo.asClass().linkHostClass(hostClass);
- // Revisit host class's members with declarations in the companion object.
- KotlinMemberInfo.markKotlinMemberInfo(hostClass, kotlinInfo, reporter);
- }
+ programClass.setKotlinInfo(kotlin.getKotlinInfo(programClass, appView));
},
executorService);
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmFieldSignatureInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmFieldSignatureInfo.java
new file mode 100644
index 0000000..0dae9cf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmFieldSignatureInfo.java
@@ -0,0 +1,53 @@
+// 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.kotlin;
+
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toRenamedDescriptorOrDefault;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import kotlinx.metadata.jvm.JvmFieldSignature;
+
+/**
+ * The JvmSignature for a method or property does not always correspond to the actual signature, see
+ * b/154201250. We therefore need to model the signature as well.
+ */
+public class KotlinJvmFieldSignatureInfo {
+
+ private final DexType type;
+ private final String name;
+
+ private KotlinJvmFieldSignatureInfo(String name, DexType type) {
+ this.name = name;
+ this.type = type;
+ }
+
+ public static KotlinJvmFieldSignatureInfo create(
+ JvmFieldSignature fieldSignature, AppView<?> appView) {
+ if (fieldSignature == null) {
+ return null;
+ }
+ return new KotlinJvmFieldSignatureInfo(
+ fieldSignature.getName(), appView.dexItemFactory().createType(fieldSignature.getDesc()));
+ }
+
+ public JvmFieldSignature rewrite(
+ DexEncodedField field, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+ String finalName = name;
+ if (field != null) {
+ String fieldName = field.field.name.toString();
+ String rewrittenName = namingLens.lookupName(field.field).toString();
+ if (!fieldName.equals(rewrittenName)) {
+ finalName = rewrittenName;
+ }
+ }
+ String defValue = appView.dexItemFactory().objectType.toDescriptorString();
+ return new JvmFieldSignature(
+ finalName, toRenamedDescriptorOrDefault(type, appView, namingLens, defValue));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
new file mode 100644
index 0000000..354364a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin;
+
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toRenamedDescriptorOrDefault;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import kotlinx.metadata.jvm.JvmMethodSignature;
+
+/**
+ * The JvmSignature for a method or property does not always correspond to the actual signature, see
+ * b/154201250. We therefore need to model the signature as well.
+ */
+public class KotlinJvmMethodSignatureInfo {
+
+ private static final List<DexType> EMPTY_PARAMETERS_LIST = ImmutableList.of();
+
+ private final String name;
+ private final DexType returnType;
+ private final List<DexType> parameters;
+
+ private KotlinJvmMethodSignatureInfo(String name, DexType returnType, List<DexType> parameters) {
+ this.name = name;
+ this.returnType = returnType;
+ this.parameters = parameters;
+ }
+
+ public static KotlinJvmMethodSignatureInfo create(
+ JvmMethodSignature methodSignature, AppView<?> appView) {
+ if (methodSignature == null) {
+ return null;
+ }
+ String kotlinDescriptor = methodSignature.getDesc();
+ String returnTypeDescriptor = DescriptorUtils.getReturnTypeDescriptor(kotlinDescriptor);
+ DexItemFactory factory = appView.dexItemFactory();
+ DexType returnType = factory.createType(returnTypeDescriptor);
+ String[] descriptors = DescriptorUtils.getArgumentTypeDescriptors(kotlinDescriptor);
+ if (descriptors.length == 0) {
+ return new KotlinJvmMethodSignatureInfo(
+ methodSignature.getName(), returnType, EMPTY_PARAMETERS_LIST);
+ }
+ ImmutableList.Builder<DexType> parameters = ImmutableList.builder();
+ for (String descriptor : descriptors) {
+ parameters.add(factory.createType(descriptor));
+ }
+ return new KotlinJvmMethodSignatureInfo(
+ methodSignature.getName(), returnType, parameters.build());
+ }
+
+ public JvmMethodSignature rewrite(
+ DexEncodedMethod method, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+ String finalName = name;
+ if (method != null) {
+ String methodName = method.method.name.toString();
+ String rewrittenName = namingLens.lookupName(method.method).toString();
+ if (!methodName.equals(rewrittenName)) {
+ finalName = rewrittenName;
+ }
+ }
+ StringBuilder descBuilder = new StringBuilder();
+ descBuilder.append("(");
+ String defValue = appView.dexItemFactory().objectType.toDescriptorString();
+ for (DexType parameter : parameters) {
+ descBuilder.append(toRenamedDescriptorOrDefault(parameter, appView, namingLens, defValue));
+ }
+ descBuilder.append(")");
+ descBuilder.append(toRenamedDescriptorOrDefault(returnType, appView, namingLens, defValue));
+ return new JvmMethodSignature(finalName, descBuilder.toString());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
new file mode 100644
index 0000000..107d197
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin;
+
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toJvmMethodSignature;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import kotlinx.metadata.KmLambda;
+import kotlinx.metadata.KmLambdaVisitor;
+import kotlinx.metadata.jvm.JvmExtensionsKt;
+import kotlinx.metadata.jvm.JvmMethodSignature;
+
+// Holds information about a KmLambda
+public class KotlinLambdaInfo {
+
+ private final KotlinFunctionInfo function;
+
+ private KotlinLambdaInfo(KotlinFunctionInfo function) {
+ this.function = function;
+ }
+
+ static KotlinLambdaInfo create(DexClass clazz, KmLambda lambda, AppView<?> appView) {
+ if (lambda == null) {
+ assert false;
+ return null;
+ }
+ JvmMethodSignature signature = JvmExtensionsKt.getSignature(lambda.function);
+ if (signature == null) {
+ assert false;
+ return null;
+ }
+ for (DexEncodedMethod method : clazz.methods()) {
+ if (toJvmMethodSignature(method.method).asString().equals(signature.asString())) {
+ KotlinFunctionInfo kotlinFunctionInfo = KotlinFunctionInfo.create(lambda.function, appView);
+ method.setKotlinMemberInfo(kotlinFunctionInfo);
+ return new KotlinLambdaInfo(kotlinFunctionInfo);
+ }
+ }
+ // TODO(b/155536535): Resolve this assert for NestTreeShakeJarVerificationTest.
+ // assert false;
+ return null;
+ }
+
+ boolean rewrite(
+ KmVisitorProviders.KmLambdaVisitorProvider visitorProvider,
+ DexClass clazz,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens namingLens) {
+ for (DexEncodedMethod method : clazz.methods()) {
+ if (method.getKotlinMemberInfo() == function) {
+ KmLambdaVisitor kmLambdaVisitor = visitorProvider.get();
+ function.rewrite(kmLambdaVisitor::visitFunction, method, appView, namingLens);
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java
deleted file mode 100644
index 142aef3..0000000
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMemberInfo.java
+++ /dev/null
@@ -1,411 +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.kotlin;
-
-import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.CONSTRUCTOR;
-import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.EXTENSION_FUNCTION;
-import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.EXTENSION_PROPERTY_GETTER;
-import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.EXTENSION_PROPERTY_SETTER;
-import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.FUNCTION;
-import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.PROPERTY_GETTER;
-import static com.android.tools.r8.kotlin.KotlinMemberInfo.MemberKind.PROPERTY_SETTER;
-import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.getJvmMethodSignature;
-import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmFieldSignature;
-import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmMethodSignature;
-import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.isExtension;
-
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.KmPropertyProcessor;
-import com.android.tools.r8.utils.Reporter;
-import com.google.common.collect.ImmutableList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import kotlinx.metadata.KmConstructor;
-import kotlinx.metadata.KmDeclarationContainer;
-import kotlinx.metadata.KmFunction;
-import kotlinx.metadata.KmProperty;
-import kotlinx.metadata.KmTypeParameter;
-import kotlinx.metadata.KmValueParameter;
-import kotlinx.metadata.jvm.JvmMethodSignature;
-
-// Provides access to field/method-level Kotlin information.
-public abstract class KotlinMemberInfo {
-
- private static final List<KotlinValueParameterInfo> EMPTY_VALUE_PARAM_INFO = ImmutableList.of();
- static final List<KotlinTypeParameterInfo> EMPTY_TYPE_PARAM_INFO = ImmutableList.of();
-
- private static final KotlinMemberInfo NO_KOTLIN_MEMBER_INFO = new NoKotlinMemberInfo();
-
- public static KotlinMemberInfo getNoKotlinMemberInfo() {
- return NO_KOTLIN_MEMBER_INFO;
- }
-
- public final MemberKind memberKind;
- // Original member flags. May be necessary to keep Kotlin-specific flag, e.g., suspend function.
- final int flags;
-
- private KotlinMemberInfo(MemberKind memberKind, int flags) {
- this.memberKind = memberKind;
- this.flags = flags;
- }
-
- public boolean isFunctionInfo() {
- return false;
- }
-
- public KotlinFunctionInfo asFunctionInfo() {
- return null;
- }
-
- public boolean isFieldInfo() {
- return false;
- }
-
- public KotlinFieldInfo asFieldInfo() {
- return null;
- }
-
- public boolean isPropertyInfo() {
- return false;
- }
-
- public KotlinPropertyInfo asPropertyInfo() {
- return null;
- }
-
- private static class NoKotlinMemberInfo extends KotlinMemberInfo {
-
- private NoKotlinMemberInfo() {
- super(MemberKind.NONE, 0);
- }
- }
-
- public static class KotlinFunctionInfo extends KotlinMemberInfo {
-
- // Information from original KmValueParameter(s) if available.
- final List<KotlinValueParameterInfo> valueParameterInfos;
- // Information from original KmFunction.returnType. Null if this is from a KmConstructor.
- public final KotlinTypeInfo returnType;
- // Information from original KmFunction.receiverType. Null if this is from a KmConstructor.
- final KotlinTypeInfo receiverParameterType;
- // Information about original type parameters. Null if this is from a KmConstructor.
- final List<KotlinTypeParameterInfo> kotlinTypeParameterInfo;
-
- private KotlinFunctionInfo(
- MemberKind memberKind,
- int flags,
- KotlinTypeInfo returnType,
- KotlinTypeInfo receiverParameterType,
- List<KotlinValueParameterInfo> valueParameterInfos,
- List<KotlinTypeParameterInfo> kotlinTypeParameterInfo) {
- super(memberKind, flags);
- assert memberKind.isFunction() || memberKind.isConstructor();
- this.returnType = returnType;
- this.receiverParameterType = receiverParameterType;
- this.valueParameterInfos = valueParameterInfos;
- this.kotlinTypeParameterInfo = kotlinTypeParameterInfo;
- }
-
- KotlinValueParameterInfo getValueParameterInfo(int i) {
- if (valueParameterInfos.isEmpty()) {
- return null;
- }
- if (i < 0 || i >= valueParameterInfos.size()) {
- return null;
- }
- return valueParameterInfos.get(i);
- }
-
- @Override
- public boolean isFunctionInfo() {
- return true;
- }
-
- @Override
- public KotlinFunctionInfo asFunctionInfo() {
- return this;
- }
- }
-
- public static class KotlinFieldInfo extends KotlinMemberInfo {
-
- // Original property name for (extension) property. Otherwise, null.
- final String propertyName;
-
- private KotlinFieldInfo(MemberKind memberKind, int flags, String propertyName) {
- super(memberKind, flags);
- this.propertyName = propertyName;
- }
-
- @Override
- public boolean isFieldInfo() {
- return true;
- }
-
- @Override
- public KotlinFieldInfo asFieldInfo() {
- return this;
- }
- }
-
- public static class KotlinPropertyInfo extends KotlinMemberInfo {
-
- // Original getter flags. E.g., for property getter.
- final int getterFlags;
-
- // Original setter flags. E.g., for property setter.
- final int setterFlags;
-
- // Original property name for (extension) property. Otherwise, null.
- final String propertyName;
-
- // Original return type information. This should never be NULL (even for setters without field).
- final KotlinTypeInfo returnType;
-
- // Information from original KmValueParameter if available.
- final KotlinValueParameterInfo valueParameterInfo;
-
- private KotlinPropertyInfo(
- MemberKind memberKind,
- int flags,
- int getterFlags,
- int setterFlags,
- String propertyName,
- KotlinTypeInfo returnType,
- KotlinValueParameterInfo valueParameterInfo) {
- super(memberKind, flags);
- this.getterFlags = getterFlags;
- this.setterFlags = setterFlags;
- this.propertyName = propertyName;
- this.returnType = returnType;
- this.valueParameterInfo = valueParameterInfo;
- }
-
- @Override
- public KotlinPropertyInfo asPropertyInfo() {
- return this;
- }
-
- @Override
- public boolean isPropertyInfo() {
- return true;
- }
- }
-
- private static KotlinFunctionInfo createFunctionInfoFromConstructor(KmConstructor kmConstructor) {
- return new KotlinFunctionInfo(
- CONSTRUCTOR,
- kmConstructor.getFlags(),
- null,
- null,
- getValueParameters(kmConstructor.getValueParameters()),
- EMPTY_TYPE_PARAM_INFO);
- }
-
- private static KotlinFunctionInfo createFunctionInfo(
- MemberKind memberKind, KmFunction kmFunction) {
- assert memberKind.isFunction();
- KotlinTypeInfo returnTypeInfo = KotlinTypeInfo.create(kmFunction.getReturnType());
- KotlinTypeInfo receiverParameterTypeInfo =
- KotlinTypeInfo.create(kmFunction.getReceiverParameterType());
- return new KotlinFunctionInfo(
- memberKind,
- kmFunction.getFlags(),
- returnTypeInfo,
- receiverParameterTypeInfo,
- getValueParameters(kmFunction.getValueParameters()),
- getTypeParameters(kmFunction.getTypeParameters()));
- }
-
- private static List<KotlinValueParameterInfo> getValueParameters(
- List<KmValueParameter> parameters) {
- if (parameters.isEmpty()) {
- return EMPTY_VALUE_PARAM_INFO;
- }
- ImmutableList.Builder<KotlinValueParameterInfo> builder = ImmutableList.builder();
- for (KmValueParameter kmValueParameter : parameters) {
- builder.add(KotlinValueParameterInfo.fromKmValueParameter(kmValueParameter));
- }
- return builder.build();
- }
-
- private static List<KotlinTypeParameterInfo> getTypeParameters(List<KmTypeParameter> parameters) {
- if (parameters.isEmpty()) {
- return EMPTY_TYPE_PARAM_INFO;
- }
- ImmutableList.Builder<KotlinTypeParameterInfo> builder = ImmutableList.builder();
- for (KmTypeParameter kmTypeParameter : parameters) {
- builder.add(KotlinTypeParameterInfo.fromKmTypeParameter(kmTypeParameter));
- }
- return builder.build();
- }
-
- private static KotlinFieldInfo createFieldInfo(MemberKind memberKind, KmProperty kmProperty) {
- assert memberKind.isBackingField() || memberKind.isBackingFieldForCompanionObject();
- return new KotlinFieldInfo(memberKind, kmProperty.getFlags(), kmProperty.getName());
- }
-
- private static KotlinPropertyInfo createPropertyInfo(
- MemberKind memberKind, KmProperty kmProperty) {
- assert memberKind.isProperty();
- return new KotlinPropertyInfo(
- memberKind,
- kmProperty.getFlags(),
- kmProperty.getGetterFlags(),
- kmProperty.getSetterFlags(),
- kmProperty.getName(),
- KotlinTypeInfo.create(kmProperty.getReturnType()),
- KotlinValueParameterInfo.fromKmValueParameter(kmProperty.getSetterParameter()));
- }
-
- public enum MemberKind {
- NONE,
-
- CONSTRUCTOR,
- FUNCTION,
- EXTENSION_FUNCTION,
-
- COMPANION_OBJECT_BACKING_FIELD,
- PROPERTY_BACKING_FIELD,
- PROPERTY_GETTER,
- PROPERTY_SETTER,
- PROPERTY_ANNOTATIONS,
-
- // No backing field for extension property.
- EXTENSION_PROPERTY_GETTER,
- EXTENSION_PROPERTY_SETTER,
- EXTENSION_PROPERTY_ANNOTATIONS;
-
- public boolean isConstructor() {
- return this == CONSTRUCTOR;
- }
-
- public boolean isFunction() {
- return this == FUNCTION || isExtensionFunction();
- }
-
- public boolean isExtensionFunction() {
- return this == EXTENSION_FUNCTION;
- }
-
- public boolean isBackingField() {
- return this == PROPERTY_BACKING_FIELD;
- }
-
- public boolean isBackingFieldForCompanionObject() {
- return this == COMPANION_OBJECT_BACKING_FIELD;
- }
-
- public boolean isProperty() {
- return isBackingField()
- || isBackingFieldForCompanionObject()
- || this == PROPERTY_GETTER
- || this == PROPERTY_SETTER
- || this == PROPERTY_ANNOTATIONS
- || isExtensionProperty();
- }
-
- public boolean isExtensionProperty() {
- return this == EXTENSION_PROPERTY_GETTER
- || this == EXTENSION_PROPERTY_SETTER
- || this == EXTENSION_PROPERTY_ANNOTATIONS;
- }
- }
-
- static void markKotlinMemberInfo(DexClass clazz, KotlinInfo kotlinInfo, Reporter reporter) {
- if (kotlinInfo == null || !kotlinInfo.hasDeclarations()) {
- return;
- }
-
- Map<String, KmConstructor> kmConstructorMap = new HashMap<>();
- Map<String, KmFunction> kmFunctionMap = new HashMap<>();
- Map<String, KmProperty> kmPropertyFieldMap = new HashMap<>();
- Map<String, KmProperty> kmPropertyGetterMap = new HashMap<>();
- Map<String, KmProperty> kmPropertySetterMap = new HashMap<>();
-
- KmDeclarationContainer kmDeclarationContainer = kotlinInfo.getDeclarations();
- String companionObject = null;
- if (kotlinInfo.isClass()) {
- companionObject = kotlinInfo.asClass().kmClass.getCompanionObject();
- kotlinInfo
- .asClass()
- .kmClass
- .getConstructors()
- .forEach(
- kmConstructor -> {
- JvmMethodSignature methodSignature = getJvmMethodSignature(kmConstructor, reporter);
- if (methodSignature != null) {
- kmConstructorMap.put(methodSignature.asString(), kmConstructor);
- }
- });
- }
- kmDeclarationContainer
- .getFunctions()
- .forEach(
- kmFunction -> {
- JvmMethodSignature methodSignature = getJvmMethodSignature(kmFunction, reporter);
- if (methodSignature != null) {
- kmFunctionMap.put(methodSignature.asString(), kmFunction);
- }
- });
- kmDeclarationContainer.getProperties().forEach(kmProperty -> {
- KmPropertyProcessor propertyProcessor = new KmPropertyProcessor(kmProperty, reporter);
- if (propertyProcessor.fieldSignature() != null) {
- kmPropertyFieldMap.put(propertyProcessor.fieldSignature().asString(), kmProperty);
- }
- if (propertyProcessor.getterSignature() != null) {
- kmPropertyGetterMap.put(propertyProcessor.getterSignature().asString(), kmProperty);
- }
- if (propertyProcessor.setterSignature() != null) {
- kmPropertySetterMap.put(propertyProcessor.setterSignature().asString(), kmProperty);
- }
- // TODO(b/151194869): property annotations
- });
-
- for (DexEncodedField field : clazz.fields()) {
- if (companionObject != null && companionObject.equals(field.field.name.toString())) {
- assert kotlinInfo.isClass();
- kotlinInfo.asClass().foundCompanionObject(field);
- continue;
- }
- String key = toJvmFieldSignature(field.field).asString();
- if (kmPropertyFieldMap.containsKey(key)) {
- KmProperty kmProperty = kmPropertyFieldMap.get(key);
- field.setKotlinMemberInfo(
- createFieldInfo(
- clazz == kotlinInfo.clazz
- ? MemberKind.PROPERTY_BACKING_FIELD
- : MemberKind.COMPANION_OBJECT_BACKING_FIELD,
- kmProperty));
- }
- }
-
- for (DexEncodedMethod method : clazz.methods()) {
- String key = toJvmMethodSignature(method.method).asString();
- if (kmConstructorMap.containsKey(key)) {
- // Interestingly we cannot assert that the method is a jvm initializer, because the jvm
- // signature can be a different.
- method.setKotlinMemberInfo(createFunctionInfoFromConstructor(kmConstructorMap.get(key)));
- } else if (kmFunctionMap.containsKey(key)) {
- KmFunction kmFunction = kmFunctionMap.get(key);
- method.setKotlinMemberInfo(
- createFunctionInfo(
- isExtension(kmFunction) ? EXTENSION_FUNCTION : FUNCTION, kmFunction));
- } else if (kmPropertyGetterMap.containsKey(key)) {
- KmProperty kmProperty = kmPropertyGetterMap.get(key);
- method.setKotlinMemberInfo(
- createPropertyInfo(
- isExtension(kmProperty) ? EXTENSION_PROPERTY_GETTER : PROPERTY_GETTER, kmProperty));
- } else if (kmPropertySetterMap.containsKey(key)) {
- KmProperty kmProperty = kmPropertySetterMap.get(key);
- method.setKotlinMemberInfo(
- createPropertyInfo(
- isExtension(kmProperty) ? EXTENSION_PROPERTY_SETTER : PROPERTY_SETTER, kmProperty));
- }
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataDiagnostic.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataDiagnostic.java
index da33940..83947d7 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataDiagnostic.java
@@ -6,8 +6,10 @@
import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
+import com.android.tools.r8.utils.StringUtils;
public class KotlinMetadataDiagnostic implements Diagnostic {
@@ -36,14 +38,39 @@
return message;
}
- static KotlinMetadataDiagnostic messageInvalidUnderlyingType(DexClass clazz, String typeAlias) {
+ static KotlinMetadataDiagnostic missingCompanionObject(
+ DexClass clazz, String companionObjectName) {
return new KotlinMetadataDiagnostic(
clazz.getOrigin(),
Position.UNKNOWN,
- "The type alias "
- + typeAlias
- + " in class "
- + clazz.type.getName()
- + " has an invalid underlying type. The type-alias is removed from the output.");
+ "The companion object "
+ + companionObjectName
+ + " could not be found in class "
+ + clazz.type.getName());
+ }
+
+ static KotlinMetadataDiagnostic unknownClassifier(String classifier) {
+ return new KotlinMetadataDiagnostic(
+ Origin.unknown(),
+ Position.UNKNOWN,
+ "The classifier " + classifier + " is unknown and cannot be parsed");
+ }
+
+ static KotlinMetadataDiagnostic invalidMethodDescriptor(String nameAndDescriptor) {
+ return new KotlinMetadataDiagnostic(
+ Origin.unknown(),
+ Position.UNKNOWN,
+ "Invalid descriptor (deserialized from Kotlin @Metadata): " + nameAndDescriptor);
+ }
+
+ static KotlinMetadataDiagnostic unexpectedErrorWhenRewriting(DexType type, Throwable t) {
+ return new KotlinMetadataDiagnostic(
+ Origin.unknown(),
+ Position.UNKNOWN,
+ "Unexpected error during rewriting of Kotlin metadata for class '"
+ + type.toSourceString()
+ + "':"
+ + StringUtils.LINE_SEPARATOR
+ + StringUtils.stacktraceAsString(t));
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java
deleted file mode 100644
index 855a71f..0000000
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataJvmExtensionUtils.java
+++ /dev/null
@@ -1,259 +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.kotlin;
-
-import static com.android.tools.r8.kotlin.Kotlin.addKotlinPrefix;
-
-import com.android.tools.r8.errors.InvalidDescriptorException;
-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.utils.DescriptorUtils;
-import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringDiagnostic;
-import com.android.tools.r8.utils.StringUtils;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-import kotlinx.metadata.KmConstructor;
-import kotlinx.metadata.KmConstructorExtensionVisitor;
-import kotlinx.metadata.KmConstructorVisitor;
-import kotlinx.metadata.KmExtensionType;
-import kotlinx.metadata.KmFunction;
-import kotlinx.metadata.KmProperty;
-import kotlinx.metadata.KmPropertyExtensionVisitor;
-import kotlinx.metadata.KmPropertyVisitor;
-import kotlinx.metadata.jvm.JvmConstructorExtensionVisitor;
-import kotlinx.metadata.jvm.JvmExtensionsKt;
-import kotlinx.metadata.jvm.JvmFieldSignature;
-import kotlinx.metadata.jvm.JvmMethodSignature;
-import kotlinx.metadata.jvm.JvmPropertyExtensionVisitor;
-
-class KotlinMetadataJvmExtensionUtils {
-
- // Mappings from Kotlin types to JVM types (of String)
- private static final Map<String, String> knownTypeConversion =
- // See {@link org.jetbrains.kotlin.metadata.jvm.deserialization.ClassMapperLite}
- ImmutableMap.<String, String>builder()
- // Boxed primitives and arrays
- .put(addKotlinPrefix("Boolean;"), "Z")
- .put(addKotlinPrefix("BooleanArray;"), "[Z")
- .put(addKotlinPrefix("Byte;"), "B")
- .put(addKotlinPrefix("ByteArray;"), "[B")
- .put(addKotlinPrefix("Char;"), "C")
- .put(addKotlinPrefix("CharArray;"), "[C")
- .put(addKotlinPrefix("Short;"), "S")
- .put(addKotlinPrefix("ShortArray;"), "[S")
- .put(addKotlinPrefix("Int;"), "I")
- .put(addKotlinPrefix("IntArray;"), "[I")
- .put(addKotlinPrefix("Long;"), "J")
- .put(addKotlinPrefix("LongArray;"), "[J")
- .put(addKotlinPrefix("Float;"), "F")
- .put(addKotlinPrefix("FloatArray;"), "[F")
- .put(addKotlinPrefix("Double;"), "D")
- .put(addKotlinPrefix("DoubleArray;"), "[D")
- // Other intrinsics
- .put(addKotlinPrefix("Unit;"), "V")
- .put(addKotlinPrefix("Any;"), "Ljava/lang/Object;")
- .put(addKotlinPrefix("Nothing;"), "Ljava/lang/Void;")
- .putAll(ImmutableList.of(
- "String", "CharSequence", "Throwable", "Cloneable", "Number", "Comparable", "Enum")
- .stream().collect(Collectors.toMap(
- t -> addKotlinPrefix(t + ";"),
- t -> "Ljava/lang/" + t + ";")))
- // Collections
- .putAll(ImmutableList.of("Iterator", "Collection", "List", "Set", "Map", "ListIterator")
- .stream().collect(Collectors.toMap(
- t -> addKotlinPrefix("collections/" + t + ";"),
- t -> "Ljava/util/" + t + ";")))
- .putAll(ImmutableList.of("Iterator", "Collection", "List", "Set", "Map", "ListIterator")
- .stream().collect(Collectors.toMap(
- t -> addKotlinPrefix("collections/Mutable" + t + ";"),
- t -> "Ljava/util/" + t + ";")))
- .put(addKotlinPrefix("collections/Iterable;"), "Ljava/lang/Iterable;")
- .put(addKotlinPrefix("collections/MutableIterable;"), "Ljava/lang/Iterable;")
- .put(addKotlinPrefix("collections/Map.Entry;"), "Ljava/util/Map$Entry;")
- .put(addKotlinPrefix("collections/MutableMap.MutableEntry;"), "Ljava/util/Map$Entry;")
- // .../FunctionN -> .../jvm/functions/FunctionN
- .putAll(
- IntStream.rangeClosed(0, 22).boxed().collect(Collectors.toMap(
- i -> addKotlinPrefix("Function" + i + ";"),
- i -> addKotlinPrefix("jvm/functions/Function" + i + ";"))))
- .build();
-
- // TODO(b/151195430): remove backward type conversions.
- private static String remapKotlinType(String type) {
- if (knownTypeConversion.containsKey(type)) {
- return knownTypeConversion.get(type);
- }
- return type;
- }
-
- // TODO(b/151195430): remove backward type conversions.
- // Kotlin @Metadata deserialization has plain "kotlin", which will be relocated in r8lib.
- // See b/70169921#comment57 for more details.
- // E.g., desc: (Labc/xyz/C;Lkotlin/Function1;)kotlin/Unit
- // remapped desc would be: (Labc/xyz/C;Lkotlin/jvm/functions/Function1;)V
- private static String remapKotlinTypeInDesc(String desc, Reporter reporter) {
- if (desc == null) {
- return null;
- }
- if (desc.isEmpty()) {
- return desc;
- }
- String[] parameterTypes;
- try {
- parameterTypes = DescriptorUtils.getArgumentTypeDescriptors(desc);
- for (int i = 0; i < parameterTypes.length; i++) {
- parameterTypes[i] = remapKotlinType(parameterTypes[i]);
- }
- } catch (InvalidDescriptorException e) {
- // JvmMethodSignature from @Metadata is not 100% reliable (due to its own optimization using
- // map, relocation in r8lib, etc.)
- reporter.info(
- new StringDiagnostic(
- "Invalid descriptor (deserialized from Kotlin @Metadata): " + desc));
- return desc;
- }
- int index = desc.indexOf(')');
- assert 0 < index && index < desc.length() : desc;
- String returnType = remapKotlinType(desc.substring(index + 1));
- return "(" + StringUtils.join(Arrays.asList(parameterTypes), "") + ")" + returnType;
- }
-
- static JvmFieldSignature toJvmFieldSignature(DexField field) {
- return new JvmFieldSignature(field.name.toString(), field.type.toDescriptorString());
- }
-
- static JvmMethodSignature toJvmMethodSignature(DexMethod method) {
- StringBuilder descBuilder = new StringBuilder();
- descBuilder.append("(");
- for (DexType argType : method.proto.parameters.values) {
- descBuilder.append(argType.toDescriptorString());
- }
- descBuilder.append(")");
- descBuilder.append(method.proto.returnType.toDescriptorString());
- return new JvmMethodSignature(method.name.toString(), descBuilder.toString());
- }
-
- static class KmConstructorProcessor {
- private JvmMethodSignature signature = null;
-
- KmConstructorProcessor(KmConstructor kmConstructor, Reporter reporter) {
- kmConstructor.accept(new KmConstructorVisitor() {
- @Override
- public KmConstructorExtensionVisitor visitExtensions(KmExtensionType type) {
- if (type != JvmConstructorExtensionVisitor.TYPE) {
- return null;
- }
- return new JvmConstructorExtensionVisitor() {
- @Override
- public void visit(JvmMethodSignature desc) {
- assert signature == null : signature.asString();
- signature = desc;
- }
- };
- }
- });
- if (signature != null) {
- String remappedDesc = remapKotlinTypeInDesc(signature.getDesc(), reporter);
- if (remappedDesc != null && !remappedDesc.equals(signature.getDesc())) {
- signature = new JvmMethodSignature(signature.getName(), remappedDesc);
- }
- }
- }
-
- JvmMethodSignature signature() {
- return signature;
- }
- }
-
- // Custom name via @JvmName("..."). Otherwise, null.
- static JvmMethodSignature getJvmMethodSignature(KmConstructor kmConstructor, Reporter reporter) {
- return remapJvmMethodSignature(JvmExtensionsKt.getSignature(kmConstructor), reporter);
- }
-
- // Custom name via @JvmName("..."). Otherwise, null.
- static JvmMethodSignature getJvmMethodSignature(KmFunction kmFunction, Reporter reporter) {
- return remapJvmMethodSignature(JvmExtensionsKt.getSignature(kmFunction), reporter);
- }
-
- private static JvmMethodSignature remapJvmMethodSignature(
- JvmMethodSignature signature, Reporter reporter) {
- if (signature != null) {
- String remappedDesc = remapKotlinTypeInDesc(signature.getDesc(), reporter);
- if (remappedDesc != null && !remappedDesc.equals(signature.getDesc())) {
- signature = new JvmMethodSignature(signature.getName(), remappedDesc);
- }
- }
- return signature;
- }
-
- static class KmPropertyProcessor {
- private JvmFieldSignature fieldSignature = null;
- // Custom getter via @get:JvmName("..."). Otherwise, null.
- private JvmMethodSignature getterSignature = null;
- // Custom getter via @set:JvmName("..."). Otherwise, null.
- private JvmMethodSignature setterSignature = null;
-
- KmPropertyProcessor(KmProperty kmProperty, Reporter reporter) {
- kmProperty.accept(new KmPropertyVisitor() {
- @Override
- public KmPropertyExtensionVisitor visitExtensions(KmExtensionType type) {
- if (type != JvmPropertyExtensionVisitor.TYPE) {
- return null;
- }
- return new JvmPropertyExtensionVisitor() {
- @Override
- public void visit(
- int flags,
- JvmFieldSignature fieldDesc,
- JvmMethodSignature getterDesc,
- JvmMethodSignature setterDesc) {
- assert fieldSignature == null : fieldSignature.asString();
- fieldSignature = fieldDesc;
- assert getterSignature == null : getterSignature.asString();
- getterSignature = getterDesc;
- assert setterSignature == null : setterSignature.asString();
- setterSignature = setterDesc;
- }
- };
- }
- });
- if (fieldSignature != null) {
- String remappedDesc = remapKotlinType(fieldSignature.getDesc());
- if (remappedDesc != null && !remappedDesc.equals(fieldSignature.getDesc())) {
- fieldSignature = new JvmFieldSignature(fieldSignature.getName(), remappedDesc);
- }
- }
- if (getterSignature != null) {
- String remappedDesc = remapKotlinTypeInDesc(getterSignature.getDesc(), reporter);
- if (remappedDesc != null && !remappedDesc.equals(getterSignature.getDesc())) {
- getterSignature = new JvmMethodSignature(getterSignature.getName(), remappedDesc);
- }
- }
- if (setterSignature != null) {
- String remappedDesc = remapKotlinTypeInDesc(setterSignature.getDesc(), reporter);
- if (remappedDesc != null && !remappedDesc.equals(setterSignature.getDesc())) {
- setterSignature = new JvmMethodSignature(setterSignature.getName(), remappedDesc);
- }
- }
- }
-
- JvmFieldSignature fieldSignature() {
- return fieldSignature;
- }
-
- JvmMethodSignature getterSignature() {
- return getterSignature;
- }
-
- JvmMethodSignature setterSignature() {
- return setterSignature;
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
index 1c597dc..b3e0eb4 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
@@ -3,18 +3,20 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
+
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationElement;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedAnnotation;
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.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueArray;
import com.android.tools.r8.graph.DexValue.DexValueInt;
import com.android.tools.r8.graph.DexValue.DexValueString;
-import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ThreadUtils;
@@ -39,7 +41,10 @@
}
public static void removeKotlinMetadataFromRenamedClass(AppView<?> appView, DexType type) {
- DexClass clazz = appView.definitionFor(type);
+ // TODO(b/154294232): Seems like this should never be called. Either we explicitly keep the
+ // class and allow obfuscation - in which we should not remove it, or we should never have
+ // metadata in the first place.
+ DexProgramClass clazz = appView.definitionForProgramType(type);
if (clazz == null) {
return;
}
@@ -52,7 +57,7 @@
// Clear associated {@link KotlinInfo} to avoid accidentally deserialize it back to
// DexAnnotation we've just removed above.
if (clazz.isProgramClass()) {
- clazz.asProgramClass().setKotlinInfo(null);
+ clazz.asProgramClass().setKotlinInfo(NO_KOTLIN_INFO);
}
}
@@ -64,35 +69,29 @@
public void run(ExecutorService executorService) throws ExecutionException {
// TODO(b/152283077): Don't disable the assert.
appView.appInfo().disableDefinitionForAssert();
- SubtypingInfo subtypingInfo = appView.appInfo().computeSubtypingInfo();
ThreadUtils.processItems(
appView.appInfo().classes(),
clazz -> {
- KotlinInfo<?> kotlinInfo = clazz.getKotlinInfo();
- if (kotlinInfo != null) {
- // If @Metadata is still associated, this class should not be renamed
- // (by {@link ClassNameMinifier} of course).
- // Or, we start maintaining @Metadata for renamed classes.
- // TODO(b/151194540): if this option is settled down, this assertion is meaningless.
- assert lens.lookupType(clazz.type, appView.dexItemFactory()) == clazz.type
- || appView.options().enableKotlinMetadataRewritingForRenamedClasses
- : clazz.toSourceString()
- + " != "
- + lens.lookupType(clazz.type, appView.dexItemFactory());
-
- DexAnnotation oldMeta =
- clazz.annotations().getFirstMatching(kotlin.metadata.kotlinMetadataType);
- // If @Metadata is already gone, e.g., by {@link AnnotationRemover} if type Metadata is
- // determined as dead (e.g., due to no keep rule), nothing to do.
- if (oldMeta == null) {
- return;
+ KotlinClassLevelInfo kotlinInfo = clazz.getKotlinInfo();
+ DexAnnotation oldMeta =
+ clazz.annotations().getFirstMatching(kotlin.metadata.kotlinMetadataType);
+ if (kotlinInfo == NO_KOTLIN_INFO) {
+ // TODO(b/154346948): Track invalid meta-data objects such that we can enable this
+ // assert oldMeta == null;
+ return;
+ }
+ if (oldMeta != null) {
+ try {
+ KotlinClassHeader kotlinClassHeader = kotlinInfo.rewrite(clazz, appView, lens);
+ DexAnnotation newMeta = createKotlinMetadataAnnotation(kotlinClassHeader);
+ clazz.setAnnotations(
+ clazz.annotations().rewrite(anno -> anno == oldMeta ? newMeta : anno));
+ } catch (Throwable t) {
+ appView
+ .options()
+ .reporter
+ .warning(KotlinMetadataDiagnostic.unexpectedErrorWhenRewriting(clazz.type, t));
}
-
- kotlinInfo.rewrite(appView, subtypingInfo, lens);
-
- DexAnnotation newMeta = createKotlinMetadataAnnotation(kotlinInfo.createHeader());
- clazz.setAnnotations(
- clazz.annotations().rewrite(anno -> anno == oldMeta ? newMeta : anno));
}
},
executorService);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
deleted file mode 100644
index 2779111..0000000
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizer.java
+++ /dev/null
@@ -1,859 +0,0 @@
-// 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.kotlin;
-
-import static com.android.tools.r8.kotlin.KotlinMemberInfo.EMPTY_TYPE_PARAM_INFO;
-import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmFieldSignature;
-import static com.android.tools.r8.kotlin.KotlinMetadataJvmExtensionUtils.toJvmMethodSignature;
-import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizerUtils.populateKmTypeFromSignature;
-import static com.android.tools.r8.kotlin.KotlinMetadataSynthesizerUtils.toClassifier;
-import static com.android.tools.r8.utils.DescriptorUtils.descriptorToKotlinClassifier;
-import static com.android.tools.r8.utils.DescriptorUtils.getBinaryNameFromDescriptor;
-import static kotlinx.metadata.Flag.Property.IS_VAR;
-import static kotlinx.metadata.FlagsKt.flagsOf;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DebugLocalInfo;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GenericSignature;
-import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
-import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
-import com.android.tools.r8.graph.GenericSignature.TypeSignature;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.kotlin.KotlinMemberInfo.KotlinFunctionInfo;
-import com.android.tools.r8.kotlin.KotlinMemberInfo.KotlinPropertyInfo;
-import com.android.tools.r8.kotlin.KotlinMetadataSynthesizerUtils.AddKotlinAnyType;
-import com.android.tools.r8.kotlin.KotlinMetadataSynthesizerUtils.KmVisitorOption;
-import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.Box;
-import com.google.common.collect.ImmutableList;
-import java.util.List;
-import java.util.function.Consumer;
-import kotlinx.metadata.KmClassifier;
-import kotlinx.metadata.KmConstructor;
-import kotlinx.metadata.KmFunction;
-import kotlinx.metadata.KmProperty;
-import kotlinx.metadata.KmType;
-import kotlinx.metadata.KmTypeParameter;
-import kotlinx.metadata.KmTypeProjection;
-import kotlinx.metadata.KmValueParameter;
-import kotlinx.metadata.KmVariance;
-import kotlinx.metadata.jvm.JvmExtensionsKt;
-
-class KotlinMetadataSynthesizer {
-
- final AppView<AppInfoWithLiveness> appView;
- final NamingLens lens;
- private final List<KmTypeParameter> classTypeParameters;
-
- public KotlinMetadataSynthesizer(
- AppView<AppInfoWithLiveness> appView, NamingLens lens, KotlinInfo<?> kotlinInfo) {
- this.appView = appView;
- this.lens = lens;
- this.classTypeParameters = kotlinInfo.getTypeParameters();
- }
-
- static boolean isExtension(KmFunction kmFunction) {
- return kmFunction.getReceiverParameterType() != null;
- }
-
- static boolean isExtension(KmProperty kmProperty) {
- return kmProperty.getReceiverParameterType() != null;
- }
-
- static KmType toKmType(String descriptor) {
- KmType kmType = new KmType(flagsOf());
- kmType.visitClass(descriptorToKotlinClassifier(descriptor));
- return kmType;
- }
-
- private DexType toRenamedType(DexType type) {
- // For library or classpath class, synthesize @Metadata always.
- // For a program class, make sure it is live.
- if (!appView.appInfo().isNonProgramTypeOrLiveProgramType(type)) {
- return null;
- }
- DexType renamedType = lens.lookupType(type, appView.dexItemFactory());
- // For library or classpath class, we should not have renamed it.
- DexClass clazz = appView.definitionFor(type);
- assert clazz == null || clazz.isProgramClass() || renamedType == type
- : type.toSourceString() + " -> " + renamedType.toSourceString();
- return renamedType;
- }
-
- String toRenamedBinaryName(DexType type) {
- DexType renamedType = toRenamedType(type);
- if (renamedType == null) {
- return null;
- }
- return getBinaryNameFromDescriptor(renamedType.toDescriptorString());
- }
-
- String toRenamedClassifier(DexType type) {
- type = type.isClassType() ? toRenamedType(type) : type;
- if (type == null) {
- return null;
- }
- return toClassifier(type, appView.dexItemFactory());
- }
-
- // TODO(b/148654451): Canonicalization?
- KmType toRenamedKmType(
- DexType type,
- TypeSignature typeSignature,
- KotlinTypeInfo originalKotlinTypeInfo,
- List<KmTypeParameter> typeParameters) {
- if (originalKotlinTypeInfo != null && originalKotlinTypeInfo.isTypeAlias()) {
- KmType kmType = new KmType(flagsOf());
- kmType.visitTypeAlias(originalKotlinTypeInfo.asTypeAlias().getName());
- return kmType;
- }
- return toRenamedKmTypeWithClassifier(
- type, originalKotlinTypeInfo, typeSignature, typeParameters);
- }
-
- private KmType toRenamedKmTypeWithClassifierForFieldSignature(
- KotlinTypeInfo originalTypeInfo,
- FieldTypeSignature fieldSignature,
- List<KmTypeParameter> typeParameters) {
- Box<KmType> kmTypeBox = new Box<>();
- populateKmTypeFromSignature(
- fieldSignature,
- originalTypeInfo,
- (kmVisitorOption) -> {
- assert kmVisitorOption == KmVisitorOption.VISIT_NEW;
- KmType value = new KmType(flagsOf());
- kmTypeBox.set(value);
- return value;
- },
- typeParameters,
- appView.dexItemFactory(),
- AddKotlinAnyType.ADD);
- return kmTypeBox.get();
- }
-
- private KmType toRenamedKmTypeWithClassifier(
- DexType type,
- KotlinTypeInfo originalTypeInfo,
- TypeSignature typeSignature,
- List<KmTypeParameter> typeParameters) {
- if (typeSignature != null && typeSignature.isFieldTypeSignature()) {
- KmType renamedKmType =
- toRenamedKmTypeWithClassifierForFieldSignature(
- originalTypeInfo, typeSignature.asFieldTypeSignature(), typeParameters);
- if (renamedKmType != null) {
- return renamedKmType;
- }
- }
- String classifier = toRenamedClassifier(type);
- if (classifier == null) {
- return null;
- }
- // Seems like no flags for KmType are ever set, thus passing in flagsOf() seems ok.
- KmType renamedKmType = new KmType(flagsOf());
- renamedKmType.visitClass(classifier);
- // TODO(b/151194164): Can be generalized too, like ArrayTypeSignature.Converter ?
- // E.g., java.lang.String[] -> KmType(kotlin/Array, KmTypeProjection(OUT, kotlin/String))
- if (type.isArrayType() && !type.isPrimitiveArrayType()) {
- DexType elementType = type.toArrayElementType(appView.dexItemFactory());
- KmType argumentType = toRenamedKmTypeWithClassifier(elementType, null, null, typeParameters);
- KmVariance variance =
- originalTypeInfo != null && originalTypeInfo.isObjectArray()
- ? originalTypeInfo.getArguments().get(0).variance
- : KmVariance.INVARIANT;
- if (variance == null) {
- variance = KmVariance.INVARIANT;
- }
- renamedKmType.getArguments().add(new KmTypeProjection(variance, argumentType));
- }
- return renamedKmType;
- }
-
- KmConstructor toRenamedKmConstructor(DexClass clazz, DexEncodedMethod method) {
- // Make sure it is an instance initializer and live.
- if (!method.isInstanceInitializer()
- || !appView.appInfo().liveMethods.contains(method.method)) {
- return null;
- }
- // Take access flags from metadata.
- KotlinFunctionInfo kotlinFunctionInfo = method.getKotlinMemberInfo().asFunctionInfo();
- int flags;
- List<KotlinTypeParameterInfo> originalTypeParameterInfo;
- if (kotlinFunctionInfo != null) {
- flags = kotlinFunctionInfo.flags;
- originalTypeParameterInfo = kotlinFunctionInfo.kotlinTypeParameterInfo;
- } else {
- flags = method.accessFlags.getAsKotlinFlags();
- originalTypeParameterInfo = EMPTY_TYPE_PARAM_INFO;
- }
- KmConstructor kmConstructor = new KmConstructor(flags);
- JvmExtensionsKt.setSignature(kmConstructor, toJvmMethodSignature(method.method));
- MethodTypeSignature signature = GenericSignature.Parser.toMethodTypeSignature(method, appView);
- List<KmTypeParameter> typeParameters =
- convertFormalTypeParameters(
- originalTypeParameterInfo,
- signature.getFormalTypeParameters(),
- kmTypeParameter -> {
- assert false : "KmConstructor cannot have additional type parameters";
- });
- List<KmValueParameter> parameters = kmConstructor.getValueParameters();
- if (!populateKmValueParameters(method, signature, parameters, typeParameters)) {
- return null;
- }
- // For inner, non-static classes, the type-parameter for the receiver should not have a
- // value-parameter:
- // val myInner : OuterNestedInner = nested.Inner(1)
- // Will have value-parameters for the constructor:
- // # constructors: KmConstructor[
- // # KmConstructor{
- // # flags: 6,
- // # valueParameters: KmValueParameter[
- // # KmValueParameter{
- // # flags: 0,
- // # name: x,
- // # type: KmType{
- // # flags: 0,
- // # classifier: Class(name=kotlin/Int),
- // # arguments: KmTypeProjection[],
- // # abbreviatedType: null,
- // # outerType: null,
- // # raw: false,
- // # annotations: KmAnnotion[],
- // # },
- // # varargElementType: null,
- // # }
- // # ],
- // # signature: <init>(Lcom/android/tools/r8/kotlin/metadata/typealias_lib/Outer$Nested;I)V,
- // # }
- // # ],
- // A bit weird since the signature obviously have two value-parameters.
- List<InnerClassAttribute> innerClasses = clazz.getInnerClasses();
- if (!parameters.isEmpty() && !innerClasses.isEmpty()) {
- DexType immediateOuterType = null;
- for (InnerClassAttribute innerClass : innerClasses) {
- if (innerClass.getInner() == clazz.type) {
- immediateOuterType = innerClass.getOuter();
- break;
- }
- }
- if (immediateOuterType != null) {
- String classifier = toRenamedClassifier(immediateOuterType);
- KmType potentialReceiver = parameters.get(0).getType();
- if (potentialReceiver != null
- && potentialReceiver.classifier instanceof KmClassifier.Class
- && ((KmClassifier.Class) potentialReceiver.classifier).getName().equals(classifier)) {
- parameters.remove(0);
- }
- }
- }
- return kmConstructor;
- }
-
- KmFunction toRenamedKmFunction(DexEncodedMethod method) {
- // For library overrides, synthesize @Metadata always.
- // For regular methods, make sure it is live or pinned.
- if (!method.isLibraryMethodOverride().isTrue()
- && !appView.appInfo().isPinned(method.method)
- && !appView.appInfo().liveMethods.contains(method.method)) {
- return null;
- }
- DexMethod renamedMethod = lens.lookupMethod(method.method, appView.dexItemFactory());
- // For a library method override, we should not have renamed it.
- assert !method.isLibraryMethodOverride().isTrue() || renamedMethod.name == method.method.name
- : method.toSourceString() + " -> " + renamedMethod.toSourceString();
- // TODO(b/151194869): Should we keep kotlin-specific flags only while synthesizing the base
- // value from general JVM flags?
- KotlinFunctionInfo kotlinMemberInfo = method.getKotlinMemberInfo().asFunctionInfo();
- assert kotlinMemberInfo != null;
- int flag =
- appView.appInfo().isPinned(method.method)
- ? kotlinMemberInfo.flags
- : method.accessFlags.getAsKotlinFlags();
- KmFunction kmFunction = new KmFunction(flag, renamedMethod.name.toString());
- JvmExtensionsKt.setSignature(kmFunction, toJvmMethodSignature(renamedMethod));
-
- // TODO(b/129925954): Should this be integrated as part of DexDefinition instead of parsing it
- // on demand? That may require utils to map internal encoding of signature back to
- // corresponding backend definitions, like DexAnnotation (DEX) or Signature attribute (CF).
- MethodTypeSignature signature = GenericSignature.Parser.toMethodTypeSignature(method, appView);
- List<KmTypeParameter> methodTypeParameters = kmFunction.getTypeParameters();
- DexProto proto = method.method.proto;
- DexType returnType = proto.returnType;
- TypeSignature returnSignature = signature.returnType().typeSignature();
- List<KmTypeParameter> allTypeParameters =
- convertFormalTypeParameters(
- kotlinMemberInfo.kotlinTypeParameterInfo,
- signature.getFormalTypeParameters(),
- methodTypeParameters::add);
- KmType kmReturnType =
- toRenamedKmType(
- returnType, returnSignature, kotlinMemberInfo.returnType, allTypeParameters);
- if (kmReturnType == null) {
- return null;
- }
- kmFunction.setReturnType(kmReturnType);
- if (method.isKotlinExtensionFunction()) {
- assert proto.parameters.values.length > 0 : method.method.toSourceString();
- DexType receiverType = proto.parameters.values[0];
- TypeSignature receiverSignature = signature.getParameterTypeSignature(0);
- KmType kmReceiverType =
- toRenamedKmType(
- receiverType,
- receiverSignature,
- kotlinMemberInfo.receiverParameterType,
- allTypeParameters);
- if (kmReceiverType == null) {
- return null;
- }
- kmFunction.setReceiverParameterType(kmReceiverType);
- }
-
- if (!populateKmValueParameters(
- method, signature, kmFunction.getValueParameters(), allTypeParameters)) {
- return null;
- }
- return kmFunction;
- }
-
- private List<KmTypeParameter> convertFormalTypeParameters(
- List<KotlinTypeParameterInfo> originalTypeParameterInfo,
- List<FormalTypeParameter> parameters,
- Consumer<KmTypeParameter> addedFromParameters) {
- return KotlinMetadataSynthesizerUtils.convertFormalTypeParameters(
- classTypeParameters,
- originalTypeParameterInfo,
- parameters,
- appView.dexItemFactory(),
- addedFromParameters);
- }
-
- private boolean populateKmValueParameters(
- DexEncodedMethod method,
- MethodTypeSignature signature,
- List<KmValueParameter> parameters,
- List<KmTypeParameter> typeParameters) {
- KotlinFunctionInfo kotlinFunctionInfo = method.getKotlinMemberInfo().asFunctionInfo();
- if (kotlinFunctionInfo == null) {
- return false;
- }
- boolean isExtension = method.isKotlinExtensionFunction();
- for (int i = isExtension ? 1 : 0; i < method.method.proto.parameters.values.length; i++) {
- DexType parameterType = method.method.proto.parameters.values[i];
- DebugLocalInfo debugLocalInfo = method.getParameterInfo().get(i);
- String parameterName = debugLocalInfo != null ? debugLocalInfo.name.toString() : ("p" + i);
- KotlinValueParameterInfo valueParameterInfo =
- kotlinFunctionInfo.getValueParameterInfo(isExtension ? i - 1 : i);
- TypeSignature parameterTypeSignature = signature.getParameterTypeSignature(i);
- KmValueParameter kmValueParameter =
- toRewrittenKmValueParameter(
- valueParameterInfo,
- parameterType,
- parameterTypeSignature,
- parameterName,
- typeParameters);
- if (kmValueParameter == null) {
- return false;
- }
- parameters.add(kmValueParameter);
- }
- return true;
- }
-
- private KmValueParameter toRewrittenKmValueParameter(
- KotlinValueParameterInfo valueParameterInfo,
- DexType parameterType,
- TypeSignature parameterTypeSignature,
- String candidateParameterName,
- List<KmTypeParameter> typeParameters) {
- int flag = valueParameterInfo != null ? valueParameterInfo.flag : flagsOf();
- String name = valueParameterInfo != null ? valueParameterInfo.name : candidateParameterName;
- KmValueParameter kmValueParameter = new KmValueParameter(flag, name);
- KotlinTypeInfo originalKmTypeInfo = valueParameterInfo != null ? valueParameterInfo.type : null;
- KmType kmParamType =
- toRenamedKmType(parameterType, parameterTypeSignature, originalKmTypeInfo, typeParameters);
- if (kmParamType == null) {
- return null;
- }
- kmValueParameter.setType(kmParamType);
- if (valueParameterInfo != null) {
- JvmExtensionsKt.getAnnotations(kmParamType).addAll(valueParameterInfo.annotations);
- }
- if (valueParameterInfo != null && valueParameterInfo.isVararg) {
- // TODO(b/152389234): Test for arrays in varargs.
- if (!parameterType.isArrayType()) {
- return null;
- }
- // vararg x: T gets translated to x: Array<out T>
- DexType elementType = parameterType.toArrayElementType(appView.dexItemFactory());
- TypeSignature elementSignature =
- parameterTypeSignature != null
- ? parameterTypeSignature.toArrayElementTypeSignature(appView) : null;
- KmType kmElementType = toRenamedKmType(elementType, elementSignature, null, typeParameters);
- if (kmElementType == null) {
- return null;
- }
- kmValueParameter.setVarargElementType(kmElementType);
- }
-
- return kmValueParameter;
- }
-
- /**
- * A group of a field, and methods that correspond to a Kotlin property.
- *
- * <p>
- * va(l|r) prop: T
- *
- * is converted to a backing field with signature `T prop` if necessary. If the property is a pure
- * computation, e.g., return `this` or access to the status of other properties, a field is not
- * needed for the property.
- * <p>
- * Depending on property visibility, getter is defined with signature `T getProp()` (or `isProp`
- * for boolean property).
- * <p>
- * If it's editable, i.e., declared as `var`, setter is defined with signature `void setProp(T)`.
- * <p>
- * Yet another addition, `void prop$annotations()`, seems(?) to be used to store associated
- * annotations.
- *
- * <p>
- * Currently, it's unclear how users can selectively keep each. Assuming every piece is kept by
- * more general rules, such as keeping non-private members, we expect to find those as a whole.
- * When rewriting a corresponding property, each information is used to update corresponding part,
- * e.g., field to update the return type of the property, getter to update jvmMethodSignature of
- * getter, and so on.
- */
- static class KmPropertyGroup {
-
- final int flags;
- final String name;
- final DexEncodedField field;
- private final DexEncodedMethod getter;
- private final KotlinPropertyInfo getterInfo;
- private final DexEncodedMethod setter;
- private final KotlinPropertyInfo setterInfo;
- final DexEncodedMethod annotations;
- final boolean isExtension;
- private final List<KmTypeParameter> classTypeParameters;
-
- private KmPropertyGroup(
- int flags,
- String name,
- DexEncodedField field,
- DexEncodedMethod getter,
- KotlinPropertyInfo getterInfo,
- DexEncodedMethod setter,
- KotlinPropertyInfo setterInfo,
- DexEncodedMethod annotations,
- boolean isExtension,
- List<KmTypeParameter> classTypeParameters) {
- this.flags = flags;
- this.name = name;
- this.field = field;
- this.getter = getter;
- this.getterInfo = getterInfo;
- this.setter = setter;
- this.setterInfo = setterInfo;
- this.annotations = annotations;
- this.isExtension = isExtension;
- this.classTypeParameters = classTypeParameters;
- }
-
- static Builder builder(int flags, String name, List<KmTypeParameter> classTypeParameters) {
- return new Builder(flags, name, classTypeParameters);
- }
-
- static class Builder {
-
- private final int flags;
- private final String name;
- private DexEncodedField field;
- private DexEncodedMethod getter;
- private KotlinPropertyInfo getterInfo;
- private DexEncodedMethod setter;
- private KotlinPropertyInfo setterInfo;
- private DexEncodedMethod annotations;
- private List<KmTypeParameter> classTypeParameters;
-
- private boolean isExtensionGetter;
- private boolean isExtensionSetter;
- private boolean isExtensionAnnotations;
-
- private Builder(int flags, String name, List<KmTypeParameter> classTypeParameters) {
- this.flags = flags;
- this.name = name;
- this.classTypeParameters = classTypeParameters;
- }
-
- Builder foundBackingField(DexEncodedField field) {
- this.field = field;
- return this;
- }
-
- Builder foundGetter(DexEncodedMethod getter, KotlinPropertyInfo propertyInfo) {
- this.getter = getter;
- this.getterInfo = propertyInfo;
- return this;
- }
-
- Builder foundSetter(DexEncodedMethod setter, KotlinPropertyInfo propertyInfo) {
- this.setter = setter;
- this.setterInfo = propertyInfo;
- return this;
- }
-
- Builder foundAnnotations(DexEncodedMethod annotations) {
- this.annotations = annotations;
- return this;
- }
-
- Builder isExtensionGetter() {
- this.isExtensionGetter = true;
- return this;
- }
-
- Builder isExtensionSetter() {
- this.isExtensionSetter = true;
- return this;
- }
-
- Builder isExtensionAnnotations() {
- this.isExtensionAnnotations = true;
- return this;
- }
-
- KmPropertyGroup build() {
- boolean isExtension = isExtensionGetter || isExtensionSetter || isExtensionAnnotations;
- // If this is an extension property, everything should be an extension.
- if (isExtension) {
- if (getter != null && !isExtensionGetter) {
- return null;
- }
- if (setter != null && !isExtensionSetter) {
- return null;
- }
- if (annotations != null && !isExtensionAnnotations) {
- return null;
- }
- }
- return new KmPropertyGroup(
- flags,
- name,
- field,
- getter,
- getterInfo,
- setter,
- setterInfo,
- annotations,
- isExtension,
- classTypeParameters);
- }
- }
-
- private String setFieldForProperty(
- KmProperty kmProperty,
- KmType defaultPropertyType,
- AppView<AppInfoWithLiveness> appView,
- NamingLens lens,
- KotlinMetadataSynthesizer synthesizer) {
- DexField renamedField = lens.lookupField(field.field, appView.dexItemFactory());
- if (kmProperty.getReturnType() == defaultPropertyType) {
- KmType kmPropertyType =
- synthesizer.toRenamedKmType(field.field.type, null, null, this.classTypeParameters);
- if (kmPropertyType != null) {
- kmProperty.setReturnType(kmPropertyType);
- }
- }
- JvmExtensionsKt.setFieldSignature(kmProperty, toJvmFieldSignature(renamedField));
- return appView.appInfo().isPinned(field.field) ? null : renamedField.name.toString();
- }
-
- private boolean setReceiverParameterTypeForExtensionProperty(
- KmProperty kmProperty,
- DexEncodedMethod method,
- AppView<AppInfoWithLiveness> appView,
- KotlinMetadataSynthesizer synthesizer) {
- if (!isExtension) {
- return true;
- }
- MethodTypeSignature signature =
- GenericSignature.Parser.toMethodTypeSignature(method, appView);
- // TODO(b/152599446): Update with type parameters for the receiver.
- List<KmTypeParameter> typeParameters =
- KotlinMetadataSynthesizerUtils.convertFormalTypeParameters(
- classTypeParameters,
- KotlinMemberInfo.EMPTY_TYPE_PARAM_INFO,
- signature.getFormalTypeParameters(),
- appView.dexItemFactory(),
- kmTypeParameter -> {});
- DexType receiverType = method.method.proto.parameters.values[0];
- TypeSignature receiverSignature = signature.getParameterTypeSignature(0);
- KmType kmReceiverType =
- synthesizer.toRenamedKmType(receiverType, receiverSignature, null, typeParameters);
- if (kmProperty.getReceiverParameterType() != null) {
- // If the receiver type for the extension property is set already make sure it's consistent.
- return KotlinMetadataSynthesizerUtils.hasEqualClassifier(
- kmReceiverType, kmProperty.getReceiverParameterType());
- }
- kmProperty.setReceiverParameterType(kmReceiverType);
- return true;
- }
-
- private String setGetterForProperty(
- KmProperty kmProperty,
- KmType defaultPropertyType,
- AppView<AppInfoWithLiveness> appView,
- NamingLens lens,
- KotlinMetadataSynthesizer synthesizer) {
- if (checkGetterCriteria() == GetterSetterCriteria.NOT_AVAILABLE) {
- // Property without getter.
- // Even though a getter does not exist, `kotlinc` still set getter flags and use them to
- // determine when to direct field access v.s. getter calls for property resolution.
- if (field != null) {
- kmProperty.setGetterFlags(field.accessFlags.getAsKotlinFlags());
- }
- return kmProperty.getName();
- }
- assert checkGetterCriteria() == GetterSetterCriteria.MET;
- assert getter != null;
- DexProto proto = getter.method.proto;
- assert proto.parameters.size() == (isExtension ? 1 : 0)
- : "checkGetterCriteria: " + this.toString();
- MethodTypeSignature signature =
- GenericSignature.Parser.toMethodTypeSignature(getter, appView);
- // TODO(b/152599446): Update with type parameters for the getter.
- List<KmTypeParameter> typeParameters =
- KotlinMetadataSynthesizerUtils.convertFormalTypeParameters(
- this.classTypeParameters,
- ImmutableList.of(),
- signature.getFormalTypeParameters(),
- appView.dexItemFactory(),
- kmTypeParameter -> {});
- DexType returnType = proto.returnType;
- TypeSignature returnSignature = signature.returnType().typeSignature();
- KmType kmPropertyType =
- synthesizer.toRenamedKmType(
- returnType, returnSignature, getterInfo.returnType, typeParameters);
- if (kmProperty.getReturnType() == defaultPropertyType) {
- // The property type is not set yet.
- kmProperty.setReturnType(kmPropertyType);
- } else if (!KotlinMetadataSynthesizerUtils.hasEqualClassifier(
- kmPropertyType, kmProperty.getReturnType())) {
- // If property type is set already (via backing field), make sure it's consistent.
- return null;
- }
- DexMethod renamedGetter = lens.lookupMethod(getter.method, appView.dexItemFactory());
- kmProperty.setGetterFlags(getter.accessFlags.getAsKotlinFlags());
- JvmExtensionsKt.setGetterSignature(kmProperty, toJvmMethodSignature(renamedGetter));
- return appView.appInfo().isPinned(getter.method) ? null : renamedGetter.name.toString();
- }
-
- private String setSetterForProperty(
- KmProperty kmProperty,
- KmType defaultPropertyType,
- AppView<AppInfoWithLiveness> appView,
- NamingLens lens,
- KotlinMetadataSynthesizer synthesizer) {
- GetterSetterCriteria criteria = checkSetterCriteria();
- if (criteria == GetterSetterCriteria.VIOLATE) {
- return kmProperty.getName();
- }
- if (criteria == GetterSetterCriteria.NOT_AVAILABLE) {
- if (field != null && IS_VAR.invoke(flags)) {
- // Editable property without setter.
- // Even though a setter does not exist, `kotlinc` still set setter flags and use them to
- // determine when to direct field access v.s. setter calls for property resolution.
- kmProperty.setSetterFlags(field.accessFlags.getAsKotlinFlags());
- }
- return kmProperty.getName();
- }
- assert criteria == GetterSetterCriteria.MET;
- assert setter != null;
- DexProto proto = setter.method.proto;
- assert proto.parameters.size() == (isExtension ? 2 : 1)
- : "checkSetterCriteria: " + this.toString();
- MethodTypeSignature signature =
- GenericSignature.Parser.toMethodTypeSignature(setter, appView);
- // TODO(b/152599446): Update with type parameters for the setter.
- List<KmTypeParameter> typeParameters =
- KotlinMetadataSynthesizerUtils.convertFormalTypeParameters(
- classTypeParameters,
- ImmutableList.of(),
- signature.getFormalTypeParameters(),
- appView.dexItemFactory(),
- kmTypeParameter -> {});
- int valueIndex = isExtension ? 1 : 0;
- DexType valueType = proto.parameters.values[valueIndex];
- TypeSignature valueSignature = signature.getParameterTypeSignature(valueIndex);
- KmType kmPropertyType =
- synthesizer.toRenamedKmType(
- valueType, valueSignature, setterInfo.returnType, typeParameters);
- if (kmProperty.getReturnType() == defaultPropertyType) {
- // The property type is not set yet.
- kmProperty.setReturnType(kmPropertyType);
- } else {
- // If property type is set already make sure it's consistent.
- if (!KotlinMetadataSynthesizerUtils.hasEqualClassifier(
- kmPropertyType, kmProperty.getReturnType())) {
- return null;
- }
- }
- assert setter.getKotlinMemberInfo().isPropertyInfo();
- KotlinValueParameterInfo valueParameterInfo =
- setter.getKotlinMemberInfo().asPropertyInfo().valueParameterInfo;
- KmValueParameter kmValueParameter =
- synthesizer.toRewrittenKmValueParameter(
- valueParameterInfo, valueType, valueSignature, "value", typeParameters);
- if (kmValueParameter != null) {
- kmProperty.setSetterParameter(kmValueParameter);
- }
- DexMethod renamedSetter = lens.lookupMethod(setter.method, appView.dexItemFactory());
- kmProperty.setSetterFlags(setterInfo.setterFlags);
- JvmExtensionsKt.setSetterSignature(kmProperty, toJvmMethodSignature(renamedSetter));
- return appView.appInfo().isPinned(setter.method) ? null : renamedSetter.name.toString();
- }
-
- KmProperty toRenamedKmProperty(KotlinMetadataSynthesizer synthesizer) {
- AppView<AppInfoWithLiveness> appView = synthesizer.appView;
- NamingLens lens = synthesizer.lens;
- KmProperty kmProperty = new KmProperty(flags, name, flagsOf(), flagsOf());
-
- // Set default values
- KmType defaultPropertyType = new KmType(flagsOf());
- kmProperty.setReturnType(defaultPropertyType);
-
- String renamedPropertyName = name;
- if (getter != null) {
- if (checkGetterCriteria() == GetterSetterCriteria.VIOLATE) {
- return null;
- }
- if (!setReceiverParameterTypeForExtensionProperty(
- kmProperty, getter, appView, synthesizer)) {
- return null;
- }
- renamedPropertyName =
- setGetterForProperty(kmProperty, defaultPropertyType, appView, lens, synthesizer);
- }
- if (setter != null) {
- if (checkSetterCriteria() == GetterSetterCriteria.VIOLATE) {
- return null;
- }
- if (!setReceiverParameterTypeForExtensionProperty(
- kmProperty, setter, appView, synthesizer)) {
- return null;
- }
- String renamedName =
- setSetterForProperty(kmProperty, defaultPropertyType, appView, lens, synthesizer);
- if (renamedPropertyName != null) {
- renamedPropertyName = renamedName;
- }
- }
- // Setting the property type from the field has to be done after the getter, otherwise we
- // may potentially loose type-argument information.
- if (field != null) {
- String renamedName =
- setFieldForProperty(kmProperty, defaultPropertyType, appView, lens, synthesizer);
- if (renamedPropertyName != null) {
- renamedPropertyName = renamedName;
- }
- }
- // Rename the property name if and only if none of participating members is pinned, and
- // any of them is indeed renamed (to a new name).
- if (renamedPropertyName != null) {
- kmProperty.setName(renamedPropertyName);
- }
- return kmProperty;
- }
-
- enum GetterSetterCriteria {
- NOT_AVAILABLE,
- MET,
- VIOLATE
- }
-
- // Getter should look like:
- // 1) T getProp(); for regular property, or
- // 2) T getProp(R); for extension property, where
- // T is a property type, and R is a receiver type.
- private GetterSetterCriteria checkGetterCriteria() {
- if (getter == null) {
- return GetterSetterCriteria.NOT_AVAILABLE;
- }
- // Property type will be checked against a backing field type if any.
- // And if they're different, we won't synthesize KmProperty for that.
- // Checking parameter size.
- if (isExtension) {
- return getter.method.proto.parameters.size() == 1
- ? GetterSetterCriteria.MET : GetterSetterCriteria.VIOLATE;
- } else {
- return getter.method.proto.parameters.size() == 0
- ? GetterSetterCriteria.MET : GetterSetterCriteria.VIOLATE;
- }
- }
-
- // Setter should look like:
- // 1) void setProp(T); for regular property, or
- // 2) void setProp(R, T); for extension property, where
- // T is a property type, and R is a receiver type.
- private GetterSetterCriteria checkSetterCriteria() {
- if (setter == null) {
- return GetterSetterCriteria.NOT_AVAILABLE;
- }
- if (!setter.method.proto.returnType.isVoidType()) {
- return GetterSetterCriteria.VIOLATE;
- }
- // Property type will be checked against a backing field type if any.
- // And if they're different, we won't synthesize KmProperty for that.
- // Plus, receiver type will be checked, too, against a getter.
- if (isExtension) {
- return setter.method.proto.parameters.size() == 2
- ? GetterSetterCriteria.MET : GetterSetterCriteria.VIOLATE;
- } else {
- return setter.method.proto.parameters.size() == 1
- ? GetterSetterCriteria.MET : GetterSetterCriteria.VIOLATE;
- }
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append("KmPropertyGroup {").append(System.lineSeparator());
- builder.append(" name: ").append(name).append(System.lineSeparator());
- builder.append(" isExtension: ").append(isExtension).append(System.lineSeparator());
- if (field != null) {
- builder.append(" field: ")
- .append(field.toSourceString())
- .append(System.lineSeparator());
- }
- if (getter != null) {
- builder
- .append(" getter: ")
- .append(getter.toSourceString())
- .append(System.lineSeparator());
- }
- if (setter != null) {
- builder
- .append(" setter: ")
- .append(setter.toSourceString())
- .append(System.lineSeparator());
- }
- if (annotations != null) {
- builder
- .append(" annotations: ")
- .append(annotations.toSourceString())
- .append(System.lineSeparator());
- }
- builder.append("}");
- return builder.toString();
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizerUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizerUtils.java
deleted file mode 100644
index e773cef..0000000
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataSynthesizerUtils.java
+++ /dev/null
@@ -1,290 +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.kotlin;
-
-import static com.android.tools.r8.kotlin.Kotlin.NAME;
-import static com.android.tools.r8.utils.DescriptorUtils.descriptorToKotlinClassifier;
-import static kotlinx.metadata.FlagsKt.flagsOf;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GenericSignature.ArrayTypeSignature;
-import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
-import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
-import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
-import com.android.tools.r8.graph.GenericSignature.TypeVariableSignature;
-import com.android.tools.r8.kotlin.Kotlin.ClassClassifiers;
-import com.google.common.collect.ImmutableList;
-import java.util.List;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import kotlinx.metadata.KmType;
-import kotlinx.metadata.KmTypeParameter;
-import kotlinx.metadata.KmTypeVisitor;
-import kotlinx.metadata.KmVariance;
-
-class KotlinMetadataSynthesizerUtils {
-
- // The KmVisitorOption is used for star projections, otherwise we will have late-init failures
- // due to visitStarProjection adding an argument.
- enum KmVisitorOption {
- VISIT_NEW,
- VISIT_PARENT
- }
-
- // The AddKotlinAnyType is carrying around information regarding the need for adding the trivial
- // type Kotlin/Any. The information is not consistently added, for example, for upper bounds
- // the trivial bound is not recorded.
- public enum AddKotlinAnyType {
- ADD,
- DISREGARD
- }
-
- static void populateKmTypeFromSignature(
- FieldTypeSignature typeSignature,
- KotlinTypeInfo originalTypeInfo,
- Function<KmVisitorOption, KmTypeVisitor> typeVisitor,
- List<KmTypeParameter> allTypeParameters,
- DexItemFactory factory,
- AddKotlinAnyType addAny) {
- if (typeSignature.isClassTypeSignature()) {
- populateKmTypeFromClassTypeSignature(
- typeSignature.asClassTypeSignature(),
- originalTypeInfo,
- typeVisitor,
- allTypeParameters,
- factory,
- addAny);
- } else if (typeSignature.isArrayTypeSignature()) {
- populateKmTypeFromArrayTypeSignature(
- typeSignature.asArrayTypeSignature(),
- originalTypeInfo,
- typeVisitor,
- allTypeParameters,
- factory,
- addAny);
- } else if (typeSignature.isTypeVariableSignature()) {
- populateKmTypeFromTypeVariableSignature(
- typeSignature.asTypeVariableSignature(), typeVisitor, allTypeParameters);
- } else {
- assert typeSignature.isStar();
- typeVisitor.apply(KmVisitorOption.VISIT_PARENT).visitStarProjection();
- }
- }
-
- private static void populateKmTypeFromTypeVariableSignature(
- TypeVariableSignature typeSignature,
- Function<KmVisitorOption, KmTypeVisitor> typeVisitor,
- List<KmTypeParameter> allTypeParameters) {
- for (KmTypeParameter typeParameter : allTypeParameters) {
- if (typeParameter
- .getName()
- .equals(typeSignature.asTypeVariableSignature().getTypeVariable())) {
- typeVisitor.apply(KmVisitorOption.VISIT_NEW).visitTypeParameter(typeParameter.getId());
- return;
- }
- }
- }
-
- private static void populateKmTypeFromArrayTypeSignature(
- ArrayTypeSignature typeSignature,
- KotlinTypeInfo originalTypeInfo,
- Function<KmVisitorOption, KmTypeVisitor> typeVisitor,
- List<KmTypeParameter> allTypeParameters,
- DexItemFactory factory,
- AddKotlinAnyType addAny) {
- ArrayTypeSignature arrayTypeSignature = typeSignature.asArrayTypeSignature();
- if (!arrayTypeSignature.elementSignature().isFieldTypeSignature()) {
- return;
- }
- KmTypeVisitor kmType = typeVisitor.apply(KmVisitorOption.VISIT_NEW);
- kmType.visitClass(ClassClassifiers.arrayBinaryName);
- KotlinTypeProjectionInfo projectionInfo =
- originalTypeInfo == null ? null : originalTypeInfo.getArgumentOrNull(0);
- populateKmTypeFromSignature(
- arrayTypeSignature.elementSignature().asFieldTypeSignature(),
- projectionInfo == null ? null : projectionInfo.typeInfo,
- (kmVisitorOption) -> {
- if (kmVisitorOption == KmVisitorOption.VISIT_PARENT) {
- assert originalTypeInfo.getArguments().size() == 1
- && originalTypeInfo.getArguments().get(0).isStarProjection();
- return kmType;
- } else {
- // TODO(b/152886451): Variance is only NULL when star projection. If that is the case
- // we should just apply starProjection.
- return kmType.visitArgument(
- flagsOf(),
- projectionInfo == null || projectionInfo.variance == null
- ? KmVariance.INVARIANT
- : projectionInfo.variance);
- }
- },
- allTypeParameters,
- factory,
- addAny);
- }
-
- private static void populateKmTypeFromClassTypeSignature(
- ClassTypeSignature typeSignature,
- KotlinTypeInfo originalTypeInfo,
- Function<KmVisitorOption, KmTypeVisitor> typeVisitor,
- List<KmTypeParameter> allTypeParameters,
- DexItemFactory factory,
- AddKotlinAnyType addAny) {
- // No need to record the trivial argument.
- if (addAny == AddKotlinAnyType.DISREGARD && factory.objectType == typeSignature.type()) {
- return;
- }
- KmTypeVisitor kmType = typeVisitor.apply(KmVisitorOption.VISIT_NEW);
- kmType.visitClass(toClassifier(typeSignature.type(), factory));
- for (int i = 0; i < typeSignature.typeArguments().size(); i++) {
- FieldTypeSignature typeArgument = typeSignature.typeArguments().get(i);
- KotlinTypeProjectionInfo projectionInfo =
- originalTypeInfo == null ? null : originalTypeInfo.getArgumentOrNull(i);
- populateKmTypeFromSignature(
- typeArgument,
- projectionInfo == null ? null : projectionInfo.typeInfo,
- (kmVisitorOption) -> {
- if (kmVisitorOption == KmVisitorOption.VISIT_PARENT) {
- assert projectionInfo == null || projectionInfo.isStarProjection();
- return kmType;
- } else {
- // TODO(b/152886451): Variance is only NULL when star projection. If that is the case
- // we should just apply starProjection.
- return kmType.visitArgument(
- flagsOf(),
- projectionInfo == null || projectionInfo.variance == null
- ? KmVariance.INVARIANT
- : projectionInfo.variance);
- }
- },
- allTypeParameters,
- factory,
- addAny);
- }
- }
-
- static String toClassifier(DexType type, DexItemFactory factory) {
- // E.g., V -> kotlin/Unit, J -> kotlin/Long, [J -> kotlin/LongArray
- if (factory.kotlin.knownTypeConversion.containsKey(type)) {
- DexType convertedType = factory.kotlin.knownTypeConversion.get(type);
- assert convertedType != null;
- return descriptorToKotlinClassifier(convertedType.toDescriptorString());
- }
- // E.g., [Ljava/lang/String; -> kotlin/Array
- if (type.isArrayType()) {
- return NAME + "/Array";
- }
- return descriptorToKotlinClassifier(type.toDescriptorString());
- }
-
- /**
- * Utility method building up all type-parameters from {@code classTypeParameters} combined with
- * {@code parameters}, where the consumer {@code addedFromParameters} is called for every
- * conversion of {@Code FormalTypeParameter} to {@Code KmTypeParameter}.
- *
- * <pre>
- * classTypeParameters: [KmTypeParameter(T), KmTypeParameter(S)]
- * parameters: [FormalTypeParameter(R)]
- * result: [KmTypeParameter(T), KmTypeParameter(S), KmTypeParameter(R)].
- * </pre>
- *
- * @param classTypeParameters
- * @param originalTypeParameterInfo
- * @param parameters
- * @param factory
- * @param addedFromParameters
- * @return
- */
- static List<KmTypeParameter> convertFormalTypeParameters(
- List<KmTypeParameter> classTypeParameters,
- List<KotlinTypeParameterInfo> originalTypeParameterInfo,
- List<FormalTypeParameter> parameters,
- DexItemFactory factory,
- Consumer<KmTypeParameter> addedFromParameters) {
- if (parameters.isEmpty()) {
- return classTypeParameters;
- }
- ImmutableList.Builder<KmTypeParameter> builder = ImmutableList.builder();
- builder.addAll(classTypeParameters);
- // Assign type-variables ids to names. All generic signatures has been minified at this point,
- // but it may be that type-variables are used before we can see them (is that allowed?).
- for (int i = 0; i < parameters.size(); i++) {
- FormalTypeParameter parameter = parameters.get(i);
- int flags =
- originalTypeParameterInfo.size() > i
- ? originalTypeParameterInfo.get(i).getFlags()
- : flagsOf();
- KmVariance variance =
- originalTypeParameterInfo.size() > i
- ? originalTypeParameterInfo.get(i).getVariance()
- : KmVariance.INVARIANT;
- KmTypeParameter element =
- new KmTypeParameter(
- flags,
- parameter.getName(),
- getNewId(originalTypeParameterInfo, parameter.getName(), i),
- variance);
- builder.add(element);
- addedFromParameters.accept(element);
- }
- ImmutableList<KmTypeParameter> allTypeParameters = builder.build();
- for (int i = 0; i < parameters.size(); i++) {
- FormalTypeParameter parameter = parameters.get(i);
- KmTypeParameter kmTypeParameter = allTypeParameters.get(classTypeParameters.size() + i);
- visitUpperBound(parameter.getClassBound(), allTypeParameters, kmTypeParameter, factory);
- if (parameter.getInterfaceBounds() != null) {
- for (FieldTypeSignature interfaceBound : parameter.getInterfaceBounds()) {
- visitUpperBound(interfaceBound, allTypeParameters, kmTypeParameter, factory);
- }
- }
- }
- return allTypeParameters;
- }
-
- // Tries to pick the id from the type-parameter name. If no such exist, compute the highest id and
- // add the index of the current argument (to ensure unique naming in sequence).
- private static int getNewId(
- List<KotlinTypeParameterInfo> typeParameterInfos, String typeVariable, int currentId) {
- int maxId = -1;
- for (KotlinTypeParameterInfo typeParameterInfo : typeParameterInfos) {
- if (typeParameterInfo.getName().equals(typeVariable)) {
- return typeParameterInfo.getId();
- }
- maxId = Math.max(maxId, typeParameterInfo.getId());
- }
- return maxId + 1 + currentId;
- }
-
- private static void visitUpperBound(
- FieldTypeSignature typeSignature,
- List<KmTypeParameter> allTypeParameters,
- KmTypeParameter parameter,
- DexItemFactory factory) {
- if (typeSignature.isUnknown()) {
- return;
- }
- populateKmTypeFromSignature(
- typeSignature,
- null,
- (kmVisitorOption) -> {
- assert kmVisitorOption == KmVisitorOption.VISIT_NEW;
- return parameter.visitUpperBound(flagsOf());
- },
- allTypeParameters,
- factory,
- AddKotlinAnyType.DISREGARD);
- }
-
- public static boolean hasEqualClassifier(KmType one, KmType other) {
- if (one == null && other == null) {
- return true;
- }
- if (one == null || other == null) {
- return false;
- }
- return KotlinClassifierInfo.equals(one.classifier, other.classifier);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
new file mode 100644
index 0000000..277201c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
@@ -0,0 +1,135 @@
+// 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.kotlin;
+
+import com.android.tools.r8.errors.InvalidDescriptorException;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.DescriptorUtils;
+import kotlinx.metadata.KmExtensionType;
+import kotlinx.metadata.KmProperty;
+import kotlinx.metadata.KmPropertyExtensionVisitor;
+import kotlinx.metadata.KmPropertyVisitor;
+import kotlinx.metadata.jvm.JvmFieldSignature;
+import kotlinx.metadata.jvm.JvmMethodSignature;
+import kotlinx.metadata.jvm.JvmPropertyExtensionVisitor;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+
+public class KotlinMetadataUtils {
+
+ public static final NoKotlinInfo NO_KOTLIN_INFO = new NoKotlinInfo();
+
+ private static class NoKotlinInfo
+ implements KotlinClassLevelInfo, KotlinFieldLevelInfo, KotlinMethodLevelInfo {
+
+ @Override
+ public KotlinClassHeader rewrite(
+ DexClass clazz, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+ throw new Unreachable("Should never be called");
+ }
+ }
+
+ static JvmFieldSignature toJvmFieldSignature(DexField field) {
+ return new JvmFieldSignature(field.name.toString(), field.type.toDescriptorString());
+ }
+
+ static JvmMethodSignature toJvmMethodSignature(DexMethod method) {
+ StringBuilder descBuilder = new StringBuilder();
+ descBuilder.append("(");
+ for (DexType argType : method.proto.parameters.values) {
+ descBuilder.append(argType.toDescriptorString());
+ }
+ descBuilder.append(")");
+ descBuilder.append(method.proto.returnType.toDescriptorString());
+ return new JvmMethodSignature(method.name.toString(), descBuilder.toString());
+ }
+
+ static class KmPropertyProcessor {
+ private JvmFieldSignature fieldSignature = null;
+ // Custom getter via @get:JvmName("..."). Otherwise, null.
+ private JvmMethodSignature getterSignature = null;
+ // Custom getter via @set:JvmName("..."). Otherwise, null.
+ private JvmMethodSignature setterSignature = null;
+
+ KmPropertyProcessor(KmProperty kmProperty) {
+ kmProperty.accept(
+ new KmPropertyVisitor() {
+ @Override
+ public KmPropertyExtensionVisitor visitExtensions(KmExtensionType type) {
+ if (type != JvmPropertyExtensionVisitor.TYPE) {
+ return null;
+ }
+ return new JvmPropertyExtensionVisitor() {
+ @Override
+ public void visit(
+ int flags,
+ JvmFieldSignature fieldDesc,
+ JvmMethodSignature getterDesc,
+ JvmMethodSignature setterDesc) {
+ assert fieldSignature == null : fieldSignature.asString();
+ fieldSignature = fieldDesc;
+ assert getterSignature == null : getterSignature.asString();
+ getterSignature = getterDesc;
+ assert setterSignature == null : setterSignature.asString();
+ setterSignature = setterDesc;
+ }
+ };
+ }
+ });
+ }
+
+ JvmFieldSignature fieldSignature() {
+ return fieldSignature;
+ }
+
+ JvmMethodSignature getterSignature() {
+ return getterSignature;
+ }
+
+ JvmMethodSignature setterSignature() {
+ return setterSignature;
+ }
+ }
+
+ static boolean isValidMethodDescriptor(String methodDescriptor) {
+ try {
+ String[] argDescriptors = DescriptorUtils.getArgumentTypeDescriptors(methodDescriptor);
+ for (String argDescriptor : argDescriptors) {
+ if (argDescriptor.charAt(0) == 'L' && !DescriptorUtils.isClassDescriptor(argDescriptor)) {
+ return false;
+ }
+ }
+ return true;
+ } catch (InvalidDescriptorException e) {
+ return false;
+ }
+ }
+
+ static String toRenamedDescriptorOrDefault(
+ DexType type,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens namingLens,
+ String defaultValue) {
+ if (appView.appInfo().wasPruned(type)) {
+ return defaultValue;
+ }
+ DexString descriptor = namingLens.lookupDescriptor(type);
+ if (descriptor != null) {
+ return descriptor.toString();
+ }
+ return defaultValue;
+ }
+
+ static String kotlinNameFromDescriptor(DexString descriptor) {
+ return DescriptorUtils.getBinaryNameFromDescriptor(descriptor.toString());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java
similarity index 66%
rename from src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
rename to src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java
index ef1d2b6..33a0aa6 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java
@@ -1,42 +1,30 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// 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.kotlin;
import static com.android.tools.r8.utils.StringUtils.LINE_SEPARATOR;
-import static kotlinx.metadata.Flag.Class.IS_COMPANION_OBJECT;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.SubtypingInfo;
-import com.android.tools.r8.kotlin.KotlinMemberInfo.KotlinFieldInfo;
-import com.android.tools.r8.kotlin.KotlinMemberInfo.KotlinPropertyInfo;
-import com.android.tools.r8.kotlin.KotlinMetadataSynthesizer.KmPropertyGroup;
-import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.utils.Action;
-import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringUtils;
-import com.google.common.collect.ImmutableList;
+import java.io.PrintStream;
import java.util.Comparator;
-import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
-import java.util.function.Predicate;
import java.util.stream.Collectors;
+import kotlinx.metadata.InconsistentKotlinMetadataException;
import kotlinx.metadata.KmAnnotation;
import kotlinx.metadata.KmAnnotationArgument;
import kotlinx.metadata.KmClass;
import kotlinx.metadata.KmConstructor;
import kotlinx.metadata.KmDeclarationContainer;
import kotlinx.metadata.KmFunction;
+import kotlinx.metadata.KmLambda;
import kotlinx.metadata.KmPackage;
import kotlinx.metadata.KmProperty;
import kotlinx.metadata.KmType;
@@ -47,243 +35,121 @@
import kotlinx.metadata.jvm.JvmExtensionsKt;
import kotlinx.metadata.jvm.JvmFieldSignature;
import kotlinx.metadata.jvm.JvmMethodSignature;
-import kotlinx.metadata.jvm.KotlinClassHeader;
import kotlinx.metadata.jvm.KotlinClassMetadata;
-// Provides access to package/class-level Kotlin information.
-public abstract class KotlinInfo<MetadataKind extends KotlinClassMetadata> {
-
- final DexClass clazz;
- private static final List<KmTypeParameter> EMPTY_TYPE_PARAMS = ImmutableList.of();
-
- KotlinInfo(MetadataKind metadata, DexClass clazz) {
- assert clazz != null;
- this.clazz = clazz;
- processMetadata(metadata);
- }
-
- // Subtypes will define how to process the given metadata.
- abstract void processMetadata(MetadataKind metadata);
-
- // Subtypes will define how to rewrite metadata after shrinking and minification.
- // Subtypes that represent subtypes of {@link KmDeclarationContainer} can use
- // {@link #rewriteDeclarationContainer} below.
- abstract void rewrite(
- AppView<AppInfoWithLiveness> appView, SubtypingInfo subtypingInfo, NamingLens lens);
-
- abstract KotlinClassHeader createHeader();
-
- public final List<KmTypeParameter> getTypeParameters() {
- if (!this.isClass()) {
- return EMPTY_TYPE_PARAMS;
- }
- return this.asClass().kmClass.getTypeParameters();
- }
-
- public enum Kind {
- Class, File, Synthetic, Part, Facade
- }
-
- public abstract Kind getKind();
-
- public boolean isClass() {
- return false;
- }
-
- public KotlinClass asClass() {
- return null;
- }
-
- public boolean isFile() {
- return false;
- }
-
- public KotlinFile asFile() {
- return null;
- }
-
- public boolean isSyntheticClass() {
- return false;
- }
-
- public KotlinSyntheticClass asSyntheticClass() {
- return null;
- }
-
- public boolean isClassPart() {
- return false;
- }
-
- public KotlinClassPart asClassPart() {
- return null;
- }
-
- public boolean isClassFacade() {
- return false;
- }
-
- public KotlinClassFacade asClassFacade() {
- return null;
- }
-
- boolean hasDeclarations() {
- return isClass() || isFile() || isClassPart();
- }
-
- KmDeclarationContainer getDeclarations() {
- if (isClass()) {
- return asClass().kmClass;
- } else if (isFile()) {
- return asFile().kmPackage;
- } else if (isClassPart()) {
- return asClassPart().kmPackage;
- } else {
- throw new Unreachable("Unexpected KotlinInfo: " + this);
- }
- }
-
- // {@link KmClass} and {@link KmPackage} are inherited from {@link KmDeclarationContainer} that
- // abstract functions and properties. Rewriting of those portions can be unified here.
- void rewriteDeclarationContainer(KotlinMetadataSynthesizer synthesizer) {
- assert clazz != null;
-
- KmDeclarationContainer kmDeclarationContainer = getDeclarations();
- rewriteFunctions(synthesizer, kmDeclarationContainer.getFunctions());
- rewriteProperties(synthesizer, kmDeclarationContainer.getProperties());
- rewriteTypeAliases(synthesizer, kmDeclarationContainer.getTypeAliases());
- }
-
- private void rewriteFunctions(KotlinMetadataSynthesizer synthesizer, List<KmFunction> functions) {
- functions.clear();
- for (DexEncodedMethod method : clazz.methods()) {
- if (method.isInitializer()) {
- continue;
- }
- if (method.isKotlinFunction() || method.isKotlinExtensionFunction()) {
- KmFunction function = synthesizer.toRenamedKmFunction(method);
- if (function != null) {
- functions.add(function);
- }
- }
- // TODO(b/151194869): What should we do for methods that fall into this category---no mark?
- }
- }
-
- private void rewriteTypeAliases(
- KotlinMetadataSynthesizer synthesizer, List<KmTypeAlias> typeAliases) {
- Iterator<KmTypeAlias> iterator = typeAliases.iterator();
- while (iterator.hasNext()) {
- KmTypeAlias typeAlias = iterator.next();
- KotlinTypeInfo expandedRenamed =
- KotlinTypeInfo.create(typeAlias.expandedType).toRenamed(synthesizer);
- if (expandedRenamed == null) {
- // If the expanded type is pruned, the type-alias is also removed. Type-aliases can refer to
- // other type-aliases in the underlying type, however, we only remove a type-alias when the
- // expanded type is removed making it impossible to construct any type that references the
- // type-alias anyway.
- // TODO(b/151719926): Add a test for the above.
- iterator.remove();
- continue;
- }
- typeAlias.setExpandedType(expandedRenamed.asKmType());
- // Modify the underlying type (right-hand side) of the type-alias.
- KotlinTypeInfo underlyingRenamed =
- KotlinTypeInfo.create(typeAlias.underlyingType).toRenamed(synthesizer);
- if (underlyingRenamed == null) {
- Reporter reporter = synthesizer.appView.options().reporter;
- reporter.warning(
- KotlinMetadataDiagnostic.messageInvalidUnderlyingType(clazz, typeAlias.getName()));
- iterator.remove();
- continue;
- }
- typeAlias.setUnderlyingType(underlyingRenamed.asKmType());
- }
- }
-
- private void rewriteProperties(
- KotlinMetadataSynthesizer synthesizer, List<KmProperty> properties) {
- Map<String, KmPropertyGroup.Builder> propertyGroupBuilderMap = new HashMap<>();
- // Backing fields for a companion object are declared in its host class.
- Iterable<DexEncodedField> fields = clazz.fields();
- Predicate<DexEncodedField> backingFieldTester = DexEncodedField::isKotlinBackingField;
- List<KmTypeParameter> classTypeParameters = getTypeParameters();
- if (isClass()) {
- KotlinClass ktClass = asClass();
- if (IS_COMPANION_OBJECT.invoke(ktClass.kmClass.getFlags()) && ktClass.hostClass != null) {
- fields = ktClass.hostClass.fields();
- backingFieldTester = DexEncodedField::isKotlinBackingFieldForCompanionObject;
- }
- }
- for (DexEncodedField field : fields) {
- if (backingFieldTester.test(field)) {
- KotlinFieldInfo kotlinFieldInfo = field.getKotlinMemberInfo().asFieldInfo();
- assert kotlinFieldInfo != null;
- String name = kotlinFieldInfo.propertyName;
- assert name != null;
- KmPropertyGroup.Builder builder =
- propertyGroupBuilderMap.computeIfAbsent(
- name,
- k -> KmPropertyGroup.builder(kotlinFieldInfo.flags, name, classTypeParameters));
- builder.foundBackingField(field);
- }
- }
- for (DexEncodedMethod method : clazz.methods()) {
- if (method.isInitializer()) {
- continue;
- }
- if (method.isKotlinProperty() || method.isKotlinExtensionProperty()) {
- assert method.getKotlinMemberInfo().isPropertyInfo();
- KotlinPropertyInfo kotlinPropertyInfo = method.getKotlinMemberInfo().asPropertyInfo();
- String name = kotlinPropertyInfo.propertyName;
- assert name != null;
- KmPropertyGroup.Builder builder =
- propertyGroupBuilderMap.computeIfAbsent(
- name,
- // Hitting here (creating a property builder) after visiting all fields means that
- // this property doesn't have a backing field. Don't use members' flags.
- k -> KmPropertyGroup.builder(kotlinPropertyInfo.flags, name, classTypeParameters));
- switch (kotlinPropertyInfo.memberKind) {
- case EXTENSION_PROPERTY_GETTER:
- builder.isExtensionGetter();
- // fallthrough;
- case PROPERTY_GETTER:
- builder.foundGetter(method, kotlinPropertyInfo);
- break;
- case EXTENSION_PROPERTY_SETTER:
- builder.isExtensionSetter();
- // fallthrough;
- case PROPERTY_SETTER:
- builder.foundSetter(method, kotlinPropertyInfo);
- break;
- case EXTENSION_PROPERTY_ANNOTATIONS:
- builder.isExtensionAnnotations();
- // fallthrough;
- case PROPERTY_ANNOTATIONS:
- builder.foundAnnotations(method);
- break;
- default:
- throw new Unreachable("Not a Kotlin property: " + method.getKotlinMemberInfo());
- }
- }
- // TODO(b/151194869): What should we do for methods that fall into this category---no mark?
- }
- properties.clear();
- for (KmPropertyGroup.Builder builder : propertyGroupBuilderMap.values()) {
- KmPropertyGroup group = builder.build();
- if (group == null) {
- continue;
- }
- KmProperty property = group.toRenamedKmProperty(synthesizer);
- if (property != null) {
- properties.add(property);
- }
- }
- }
-
- public abstract String toString(String indent);
+public class KotlinMetadataWriter {
static final String INDENT = " ";
+ public static void writeKotlinMetadataAnnotation(
+ String prefix, DexAnnotation annotation, PrintStream ps, Kotlin kotlin) {
+ assert annotation.annotation.type == kotlin.metadata.kotlinMetadataType;
+ try {
+ KotlinClassMetadata kMetadata =
+ KotlinClassMetadataReader.toKotlinClassMetadata(kotlin, annotation.annotation);
+ ps.println(kotlinMetadataToString(prefix, kMetadata));
+ } catch (Throwable ignored) {
+ }
+ }
+
+ public static String kotlinMetadataToString(String prefix, KotlinClassMetadata kMetadata) {
+ if (kMetadata instanceof KotlinClassMetadata.Class) {
+ return kotlinClassMetadataToString((KotlinClassMetadata.Class) kMetadata, prefix);
+ } else if (kMetadata instanceof KotlinClassMetadata.FileFacade) {
+ // e.g., B.kt becomes class `BKt`
+ return kotlinFileFacadeMetadataToString((KotlinClassMetadata.FileFacade) kMetadata, prefix);
+ } else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassFacade) {
+ // multi-file class with the same @JvmName.
+ return kotlinMultiFileClassFacadeMetadataString(
+ (KotlinClassMetadata.MultiFileClassFacade) kMetadata, prefix);
+ } else if (kMetadata instanceof KotlinClassMetadata.MultiFileClassPart) {
+ // A single file, which is part of multi-file class.
+ return kotlinMultiFileClassPartToString(
+ (KotlinClassMetadata.MultiFileClassPart) kMetadata, prefix);
+ } else if (kMetadata instanceof KotlinClassMetadata.SyntheticClass) {
+ return kotlinSyntheticClassToString((KotlinClassMetadata.SyntheticClass) kMetadata, prefix);
+ } else {
+ throw new Unreachable("An error would be thrown before in createKotlinInfo");
+ }
+ }
+
+ private static String kotlinClassMetadataToString(
+ KotlinClassMetadata.Class kMetadata, String indent) {
+ StringBuilder sb = new StringBuilder(indent);
+ KotlinMetadataWriter.appendKmSection(
+ indent,
+ "Metadata.Class",
+ sb,
+ newIndent -> {
+ KotlinMetadataWriter.appendKmClass(newIndent, sb, kMetadata.toKmClass());
+ });
+ return sb.toString();
+ }
+
+ private static String kotlinFileFacadeMetadataToString(
+ KotlinClassMetadata.FileFacade kMetadata, String indent) {
+ StringBuilder sb = new StringBuilder(indent);
+ KotlinMetadataWriter.appendKmSection(
+ indent,
+ "Metadata.FileFacade",
+ sb,
+ newIndent -> {
+ KotlinMetadataWriter.appendKmPackage(newIndent, sb, kMetadata.toKmPackage());
+ });
+ return sb.toString();
+ }
+
+ private static String kotlinMultiFileClassFacadeMetadataString(
+ KotlinClassMetadata.MultiFileClassFacade kMetadata, String indent) {
+ return indent
+ + "MetaData.MultiFileClassFacade("
+ + StringUtils.join(kMetadata.getPartClassNames(), ", ")
+ + ")";
+ }
+
+ private static String kotlinMultiFileClassPartToString(
+ KotlinClassMetadata.MultiFileClassPart kMetadata, String indent) {
+ StringBuilder sb = new StringBuilder(indent);
+ KotlinMetadataWriter.appendKmSection(
+ indent,
+ "Metadata.MultiFileClassPart",
+ sb,
+ newIndent -> {
+ KotlinMetadataWriter.appendKeyValue(
+ newIndent, "facadeClassName", sb, kMetadata.getFacadeClassName());
+ KotlinMetadataWriter.appendKmPackage(newIndent, sb, kMetadata.toKmPackage());
+ });
+ return sb.toString();
+ }
+
+ private static String kotlinSyntheticClassToString(
+ KotlinClassMetadata.SyntheticClass kMetadata, String indent) {
+ StringBuilder sb = new StringBuilder();
+ KotlinMetadataWriter.appendKmSection(
+ indent,
+ "Metadata.SyntheticClass",
+ sb,
+ newIndent -> {
+ try {
+ KmLambda kmLambda = kMetadata.toKmLambda();
+ if (kmLambda != null) {
+ KotlinMetadataWriter.appendKeyValue(
+ newIndent,
+ "function",
+ sb,
+ nextIndent -> {
+ KotlinMetadataWriter.appendKmFunction(nextIndent, sb, kmLambda.function);
+ });
+ } else {
+ KotlinMetadataWriter.appendKeyValue(newIndent, "function", sb, "null");
+ }
+ } catch (InconsistentKotlinMetadataException ex) {
+ appendKeyValue(newIndent, "function", sb, ex.getMessage());
+ }
+ });
+ return sb.toString();
+ }
+
private static <T> void appendKmHelper(
String key, StringBuilder sb, Action appendContent, String start, String end) {
sb.append(key);
@@ -292,7 +158,7 @@
sb.append(end);
}
- static <T> void appendKmSection(
+ public static <T> void appendKmSection(
String indent, String typeDescription, StringBuilder sb, Consumer<String> appendContent) {
appendKmHelper(
typeDescription,
@@ -302,7 +168,7 @@
indent + "}");
}
- static <T> void appendKmList(
+ private static <T> void appendKmList(
String indent,
String typeDescription,
StringBuilder sb,
@@ -326,18 +192,18 @@
indent + "]");
}
- static void appendKeyValue(
+ private static void appendKeyValue(
String indent, String key, StringBuilder sb, Consumer<String> appendValue) {
sb.append(indent);
appendKmHelper(key, sb, () -> appendValue.accept(indent), ": ", "," + LINE_SEPARATOR);
}
- static void appendKeyValue(String indent, String key, StringBuilder sb, String value) {
+ public static void appendKeyValue(String indent, String key, StringBuilder sb, String value) {
sb.append(indent);
appendKmHelper(key, sb, () -> sb.append(value), ": ", "," + LINE_SEPARATOR);
}
- static void appendKmDeclarationContainer(
+ private static void appendKmDeclarationContainer(
String indent, StringBuilder sb, KmDeclarationContainer container) {
appendKeyValue(
indent,
@@ -349,7 +215,9 @@
"KmFunction",
sb,
container.getFunctions().stream()
- .sorted(Comparator.comparing(KmFunction::getName))
+ .sorted(
+ Comparator.comparing(
+ kmFunction -> JvmExtensionsKt.getSignature(kmFunction).asString()))
.collect(Collectors.toList()),
(nextIndent, kmFunction) -> {
appendKmFunction(nextIndent, sb, kmFunction);
@@ -365,7 +233,25 @@
"KmProperty",
sb,
container.getProperties().stream()
- .sorted(Comparator.comparing(KmProperty::getName))
+ .sorted(
+ Comparator.comparing(
+ kmProperty -> {
+ JvmMethodSignature signature =
+ JvmExtensionsKt.getGetterSignature(kmProperty);
+ if (signature != null) {
+ return signature.asString();
+ }
+ signature = JvmExtensionsKt.getSetterSignature(kmProperty);
+ if (signature != null) {
+ return signature.asString();
+ }
+ JvmFieldSignature fieldSignature =
+ JvmExtensionsKt.getFieldSignature(kmProperty);
+ if (fieldSignature != null) {
+ return fieldSignature.asString();
+ }
+ return kmProperty.getName();
+ }))
.collect(Collectors.toList()),
(nextIndent, kmProperty) -> {
appendKmProperty(nextIndent, sb, kmProperty);
@@ -389,7 +275,7 @@
});
}
- static void appendKmPackage(String indent, StringBuilder sb, KmPackage kmPackage) {
+ public static void appendKmPackage(String indent, StringBuilder sb, KmPackage kmPackage) {
appendKmDeclarationContainer(indent, sb, kmPackage);
appendKeyValue(indent, "moduleName", sb, JvmExtensionsKt.getModuleName(kmPackage));
appendKeyValue(
@@ -408,7 +294,7 @@
});
}
- static void appendKmClass(String indent, StringBuilder sb, KmClass kmClass) {
+ public static void appendKmClass(String indent, StringBuilder sb, KmClass kmClass) {
appendKeyValue(indent, "flags", sb, kmClass.getFlags() + "");
appendKeyValue(indent, "name", sb, kmClass.getName());
appendKeyValue(
@@ -473,7 +359,11 @@
newIndent,
"KmConstructor",
sb,
- kmClass.getConstructors(),
+ kmClass.getConstructors().stream()
+ .sorted(
+ Comparator.comparing(
+ kmConstructor -> JvmExtensionsKt.getSignature(kmConstructor).asString()))
+ .collect(Collectors.toList()),
(nextIndent, constructor) -> {
appendKmConstructor(nextIndent, sb, constructor);
});
@@ -501,7 +391,7 @@
});
}
- private static void appendKmFunction(String indent, StringBuilder sb, KmFunction function) {
+ public static void appendKmFunction(String indent, StringBuilder sb, KmFunction function) {
appendKmSection(
indent,
"KmFunction",
@@ -835,9 +725,4 @@
});
});
}
-
- @Override
- public String toString() {
- return toString("");
- }
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMethodLevelInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMethodLevelInfo.java
new file mode 100644
index 0000000..604374e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMethodLevelInfo.java
@@ -0,0 +1,32 @@
+// 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.kotlin;
+
+public interface KotlinMethodLevelInfo {
+
+ default boolean isConstructor() {
+ return false;
+ }
+
+ default KotlinConstructorInfo asConstructor() {
+ return null;
+ }
+
+ default boolean isFunction() {
+ return false;
+ }
+
+ default KotlinFunctionInfo asFunction() {
+ return null;
+ }
+
+ default boolean isProperty() {
+ return false;
+ }
+
+ default KotlinPropertyInfo asProperty() {
+ return null;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java
new file mode 100644
index 0000000..02efea2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassFacadeInfo.java
@@ -0,0 +1,66 @@
+// 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.kotlin;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+import kotlinx.metadata.jvm.KotlinClassMetadata.MultiFileClassFacade;
+
+// Holds information about Metadata.MultiFileClassFace
+public class KotlinMultiFileClassFacadeInfo implements KotlinClassLevelInfo {
+
+ private final List<DexType> partClassNames;
+
+ private KotlinMultiFileClassFacadeInfo(List<DexType> partClassNames) {
+ this.partClassNames = partClassNames;
+ }
+
+ static KotlinMultiFileClassFacadeInfo create(
+ MultiFileClassFacade kmMultiFileClassFacade, AppView<?> appView) {
+ ImmutableList.Builder<DexType> builder = ImmutableList.builder();
+ for (String partClassName : kmMultiFileClassFacade.getPartClassNames()) {
+ String descriptor = DescriptorUtils.getDescriptorFromClassBinaryName(partClassName);
+ DexType type = appView.dexItemFactory().createType(descriptor);
+ builder.add(type);
+ }
+ return new KotlinMultiFileClassFacadeInfo(builder.build());
+ }
+
+ @Override
+ public boolean isMultiFileFacade() {
+ return true;
+ }
+
+ @Override
+ public KotlinMultiFileClassFacadeInfo asMultiFileFacade() {
+ return this;
+ }
+
+ @Override
+ public KotlinClassHeader rewrite(
+ DexClass clazz, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+ KotlinClassMetadata.MultiFileClassFacade.Writer writer =
+ new KotlinClassMetadata.MultiFileClassFacade.Writer();
+ List<String> partClassNameStrings = new ArrayList<>(partClassNames.size());
+ for (DexType partClassName : partClassNames) {
+ if (appView.definitionFor(partClassName) != null) {
+ DexString descriptor = namingLens.lookupDescriptor(partClassName);
+ String classifier = DescriptorUtils.descriptorToKotlinClassifier(descriptor.toString());
+ partClassNameStrings.add(classifier);
+ }
+ }
+ return writer.write(partClassNameStrings).getHeader();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
new file mode 100644
index 0000000..69ea8e4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
@@ -0,0 +1,54 @@
+// 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.kotlin;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import kotlinx.metadata.KmPackage;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+import kotlinx.metadata.jvm.KotlinClassMetadata.MultiFileClassPart;
+
+// Holds information about Metadata.MultiFileClassPartInfo
+public class KotlinMultiFileClassPartInfo implements KotlinClassLevelInfo {
+
+ private final String facadeClassName;
+ private final KotlinPackageInfo packageInfo;
+
+ private KotlinMultiFileClassPartInfo(String facadeClassName, KotlinPackageInfo packageInfo) {
+ this.facadeClassName = facadeClassName;
+ this.packageInfo = packageInfo;
+ }
+
+ static KotlinMultiFileClassPartInfo create(
+ MultiFileClassPart classPart, DexClass clazz, AppView<?> appView) {
+ return new KotlinMultiFileClassPartInfo(
+ classPart.getFacadeClassName(),
+ KotlinPackageInfo.create(classPart.toKmPackage(), clazz, appView));
+ }
+
+ @Override
+ public boolean isMultiFileClassPart() {
+ return true;
+ }
+
+ @Override
+ public KotlinMultiFileClassPartInfo asMultiFileClassPart() {
+ return this;
+ }
+
+ @Override
+ public KotlinClassHeader rewrite(
+ DexClass clazz, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+ KotlinClassMetadata.MultiFileClassPart.Writer writer =
+ new KotlinClassMetadata.MultiFileClassPart.Writer();
+ KmPackage kmPackage = new KmPackage();
+ packageInfo.rewrite(kmPackage, clazz, appView, namingLens);
+ kmPackage.accept(writer);
+ return writer.write(facadeClassName).getHeader();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
new file mode 100644
index 0000000..c90d673
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
@@ -0,0 +1,60 @@
+// 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.kotlin;
+
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toJvmFieldSignature;
+import static com.android.tools.r8.kotlin.KotlinMetadataUtils.toJvmMethodSignature;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.HashMap;
+import java.util.Map;
+import kotlinx.metadata.KmPackage;
+import kotlinx.metadata.jvm.JvmExtensionsKt;
+
+// Holds information about a KmPackage object.
+public class KotlinPackageInfo {
+
+ private final String moduleName;
+ private final KotlinDeclarationContainerInfo containerInfo;
+
+ private KotlinPackageInfo(String moduleName, KotlinDeclarationContainerInfo containerInfo) {
+ this.moduleName = moduleName;
+ this.containerInfo = containerInfo;
+ }
+
+ public static KotlinPackageInfo create(KmPackage kmPackage, DexClass clazz, AppView<?> appView) {
+ Map<String, DexEncodedField> fieldMap = new HashMap<>();
+ for (DexEncodedField field : clazz.fields()) {
+ fieldMap.put(toJvmFieldSignature(field.field).asString(), field);
+ }
+ Map<String, DexEncodedMethod> methodMap = new HashMap<>();
+ for (DexEncodedMethod method : clazz.methods()) {
+ methodMap.put(toJvmMethodSignature(method.method).asString(), method);
+ }
+ return new KotlinPackageInfo(
+ JvmExtensionsKt.getModuleName(kmPackage),
+ KotlinDeclarationContainerInfo.create(kmPackage, methodMap, fieldMap, appView));
+ }
+
+ public void rewrite(
+ KmPackage kmPackage,
+ DexClass clazz,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens namingLens) {
+ containerInfo.rewrite(
+ kmPackage::visitFunction,
+ kmPackage::visitProperty,
+ kmPackage::visitTypeAlias,
+ clazz,
+ appView,
+ namingLens);
+ JvmExtensionsKt.setModuleName(kmPackage, moduleName);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
new file mode 100644
index 0000000..a94b6fc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinPropertyInfo.java
@@ -0,0 +1,157 @@
+// 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.kotlin;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.List;
+import kotlinx.metadata.KmProperty;
+import kotlinx.metadata.KmPropertyVisitor;
+import kotlinx.metadata.jvm.JvmExtensionsKt;
+import kotlinx.metadata.jvm.JvmPropertyExtensionVisitor;
+
+// Holds information about KmProperty
+public class KotlinPropertyInfo implements KotlinFieldLevelInfo, KotlinMethodLevelInfo {
+
+ // Original flags.
+ final int flags;
+
+ // Original getter flags. E.g., for property getter.
+ final int getterFlags;
+
+ // Original setter flags. E.g., for property setter.
+ final int setterFlags;
+
+ // Original property name for (extension) property. Otherwise, null.
+ final String name;
+
+ // Original return type information. This should never be NULL (even for setters without field).
+ final KotlinTypeInfo returnType;
+
+ final KotlinTypeInfo receiverParameterType;
+
+ final KotlinValueParameterInfo setterParameter;
+
+ final List<KotlinTypeParameterInfo> typeParameters;
+
+ final int jvmFlags;
+
+ final KotlinJvmFieldSignatureInfo fieldSignature;
+
+ final KotlinJvmMethodSignatureInfo getterSignature;
+
+ final KotlinJvmMethodSignatureInfo setterSignature;
+
+ final KotlinJvmMethodSignatureInfo syntheticMethodForAnnotations;
+
+ private KotlinPropertyInfo(
+ int flags,
+ int getterFlags,
+ int setterFlags,
+ String name,
+ KotlinTypeInfo returnType,
+ KotlinTypeInfo receiverParameterType,
+ KotlinValueParameterInfo setterParameter,
+ List<KotlinTypeParameterInfo> typeParameters,
+ int jvmFlags,
+ KotlinJvmFieldSignatureInfo fieldSignature,
+ KotlinJvmMethodSignatureInfo getterSignature,
+ KotlinJvmMethodSignatureInfo setterSignature,
+ KotlinJvmMethodSignatureInfo syntheticMethodForAnnotations) {
+ this.flags = flags;
+ this.getterFlags = getterFlags;
+ this.setterFlags = setterFlags;
+ this.name = name;
+ this.returnType = returnType;
+ this.receiverParameterType = receiverParameterType;
+ this.setterParameter = setterParameter;
+ this.typeParameters = typeParameters;
+ this.jvmFlags = jvmFlags;
+ this.fieldSignature = fieldSignature;
+ this.getterSignature = getterSignature;
+ this.setterSignature = setterSignature;
+ this.syntheticMethodForAnnotations = syntheticMethodForAnnotations;
+ }
+
+ public static KotlinPropertyInfo create(KmProperty kmProperty, AppView<?> appView) {
+ return new KotlinPropertyInfo(
+ kmProperty.getFlags(),
+ kmProperty.getGetterFlags(),
+ kmProperty.getSetterFlags(),
+ kmProperty.getName(),
+ KotlinTypeInfo.create(kmProperty.getReturnType(), appView),
+ KotlinTypeInfo.create(kmProperty.getReceiverParameterType(), appView),
+ KotlinValueParameterInfo.create(kmProperty.getSetterParameter(), appView),
+ KotlinTypeParameterInfo.create(kmProperty.getTypeParameters(), appView),
+ JvmExtensionsKt.getJvmFlags(kmProperty),
+ KotlinJvmFieldSignatureInfo.create(JvmExtensionsKt.getFieldSignature(kmProperty), appView),
+ KotlinJvmMethodSignatureInfo.create(
+ JvmExtensionsKt.getGetterSignature(kmProperty), appView),
+ KotlinJvmMethodSignatureInfo.create(
+ JvmExtensionsKt.getSetterSignature(kmProperty), appView),
+ KotlinJvmMethodSignatureInfo.create(
+ JvmExtensionsKt.getSyntheticMethodForAnnotations(kmProperty), appView));
+ }
+
+ @Override
+ public boolean isFieldProperty() {
+ return true;
+ }
+
+ @Override
+ public KotlinPropertyInfo asFieldProperty() {
+ return this;
+ }
+
+ @Override
+ public boolean isProperty() {
+ return true;
+ }
+
+ @Override
+ public KotlinPropertyInfo asProperty() {
+ return this;
+ }
+
+ void rewrite(
+ KmVisitorProviders.KmPropertyVisitorProvider visitorProvider,
+ DexEncodedField field,
+ DexEncodedMethod getter,
+ DexEncodedMethod setter,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens namingLens) {
+ // TODO(b/154348683): Flags again.
+ KmPropertyVisitor kmProperty = visitorProvider.get(flags, name, getterFlags, setterFlags);
+ // TODO(b/154348149): ReturnType could have been merged to a subtype.
+ if (returnType != null) {
+ returnType.rewrite(kmProperty::visitReturnType, appView, namingLens);
+ }
+ if (receiverParameterType != null) {
+ receiverParameterType.rewrite(kmProperty::visitReceiverParameterType, appView, namingLens);
+ }
+ if (setterParameter != null) {
+ setterParameter.rewrite(kmProperty::visitSetterParameter, appView, namingLens);
+ }
+ for (KotlinTypeParameterInfo typeParameter : typeParameters) {
+ typeParameter.rewrite(kmProperty::visitTypeParameter, appView, namingLens);
+ }
+ JvmPropertyExtensionVisitor extensionVisitor =
+ (JvmPropertyExtensionVisitor) kmProperty.visitExtensions(JvmPropertyExtensionVisitor.TYPE);
+ if (extensionVisitor != null) {
+ extensionVisitor.visit(
+ jvmFlags,
+ fieldSignature == null ? null : fieldSignature.rewrite(field, appView, namingLens),
+ getterSignature == null ? null : getterSignature.rewrite(getter, appView, namingLens),
+ setterSignature == null ? null : setterSignature.rewrite(setter, appView, namingLens));
+ if (syntheticMethodForAnnotations != null) {
+ extensionVisitor.visitSyntheticMethodForAnnotations(
+ syntheticMethodForAnnotations.rewrite(null, appView, namingLens));
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClass.java b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClass.java
deleted file mode 100644
index ced14b9..0000000
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClass.java
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright (c) 2018, 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.kotlin;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.SubtypingInfo;
-import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import kotlinx.metadata.jvm.KotlinClassHeader;
-import kotlinx.metadata.jvm.KotlinClassMetadata;
-
-public final class KotlinSyntheticClass extends KotlinInfo<KotlinClassMetadata.SyntheticClass> {
- // TODO(b/151194794): Once converted to internal data structure, this can be gone.
- private KotlinClassMetadata.SyntheticClass metadata;
-
- public enum Flavour {
- KotlinStyleLambda,
- JavaStyleLambda,
- Unclassified
- }
-
- private final Flavour flavour;
-
- static KotlinSyntheticClass fromKotlinClassMetadata(
- KotlinClassMetadata kotlinClassMetadata, Kotlin kotlin, DexClass clazz) {
- assert kotlinClassMetadata instanceof KotlinClassMetadata.SyntheticClass;
- KotlinClassMetadata.SyntheticClass syntheticClass =
- (KotlinClassMetadata.SyntheticClass) kotlinClassMetadata;
- if (isKotlinStyleLambda(syntheticClass, kotlin, clazz)) {
- return new KotlinSyntheticClass(Flavour.KotlinStyleLambda, syntheticClass, clazz);
- } else if (isJavaStyleLambda(syntheticClass, kotlin, clazz)) {
- return new KotlinSyntheticClass(Flavour.JavaStyleLambda, syntheticClass, clazz);
- } else {
- return new KotlinSyntheticClass(Flavour.Unclassified, syntheticClass, clazz);
- }
- }
-
- private KotlinSyntheticClass(
- Flavour flavour, KotlinClassMetadata.SyntheticClass metadata, DexClass clazz) {
- super(metadata, clazz);
- this.flavour = flavour;
- }
-
- @Override
- void processMetadata(KotlinClassMetadata.SyntheticClass metadata) {
- this.metadata = metadata;
- if (metadata.isLambda()) {
- // TODO(b/151194794): Use #toKmLambda to store a mutable model if needed.
- }
- }
-
- @Override
- void rewrite(AppView<AppInfoWithLiveness> appView, SubtypingInfo subtypingInfo, NamingLens lens) {
- // TODO(b/151194794): no idea yet!
- assert lens.lookupType(clazz.type, appView.dexItemFactory()) == clazz.type
- || appView.options().enableKotlinMetadataRewritingForRenamedClasses
- : toString();
- }
-
- @Override
- KotlinClassHeader createHeader() {
- // TODO(b/151194794): may need to update if `rewrite` is implemented.
- return metadata.getHeader();
- }
-
- public boolean isLambda() {
- return isKotlinStyleLambda() || isJavaStyleLambda();
- }
-
- public boolean isKotlinStyleLambda() {
- return flavour == Flavour.KotlinStyleLambda;
- }
-
- public boolean isJavaStyleLambda() {
- return flavour == Flavour.JavaStyleLambda;
- }
-
- @Override
- public final Kind getKind() {
- return Kind.Synthetic;
- }
-
- @Override
- public final boolean isSyntheticClass() {
- return true;
- }
-
- @Override
- public KotlinSyntheticClass asSyntheticClass() {
- return this;
- }
-
- /**
- * Returns {@code true} if the given {@link DexClass} is a Kotlin-style lambda:
- * a class that
- * 1) is recognized as lambda in its Kotlin metadata;
- * 2) directly extends kotlin.jvm.internal.Lambda
- */
- private static boolean isKotlinStyleLambda(
- KotlinClassMetadata.SyntheticClass metadata, Kotlin kotlin, DexClass clazz) {
- return metadata.isLambda()
- && clazz.superType == kotlin.functional.lambdaType;
- }
-
- /**
- * Returns {@code true} if the given {@link DexClass} is a Java-style lambda:
- * a class that
- * 1) is recognized as lambda in its Kotlin metadata;
- * 2) doesn't extend any other class;
- * 3) directly implements only one Java SAM.
- */
- private static boolean isJavaStyleLambda(
- KotlinClassMetadata.SyntheticClass metadata, Kotlin kotlin, DexClass clazz) {
- return metadata.isLambda()
- && clazz.superType == kotlin.factory.objectType
- && clazz.interfaces.size() == 1;
- }
-
- @Override
- public String toString(String indent) {
- return indent + "Metadata.SyntheticClass { function: " + metadata.toString() + "}";
- }
-}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
new file mode 100644
index 0000000..8dfe761
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinSyntheticClassInfo.java
@@ -0,0 +1,108 @@
+// 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.kotlin;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import kotlinx.metadata.InconsistentKotlinMetadataException;
+import kotlinx.metadata.KmLambda;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+import kotlinx.metadata.jvm.KotlinClassMetadata.SyntheticClass;
+import kotlinx.metadata.jvm.KotlinClassMetadata.SyntheticClass.Writer;
+
+// Holds information about a Metadata.SyntheticClass object.
+public class KotlinSyntheticClassInfo implements KotlinClassLevelInfo {
+
+ private final KotlinLambdaInfo lambda;
+
+ public enum Flavour {
+ KotlinStyleLambda,
+ JavaStyleLambda,
+ Unclassified
+ }
+
+ private final Flavour flavour;
+
+ private KotlinSyntheticClassInfo(KotlinLambdaInfo lambda, Flavour flavour) {
+ this.lambda = lambda;
+ this.flavour = flavour;
+ }
+
+ static KotlinSyntheticClassInfo create(
+ SyntheticClass syntheticClass, DexClass clazz, Kotlin kotlin, AppView<?> appView) {
+ KmLambda lambda = null;
+ if (syntheticClass.isLambda()) {
+ try {
+ lambda = syntheticClass.toKmLambda();
+ assert lambda != null;
+ } catch (InconsistentKotlinMetadataException ex) {
+ // TODO(b/155534905): Gracefully handle these errors by retaining the original object.
+ }
+ }
+ return new KotlinSyntheticClassInfo(
+ lambda != null ? KotlinLambdaInfo.create(clazz, lambda, appView) : null,
+ getFlavour(syntheticClass, clazz, kotlin));
+ }
+
+ public boolean isLambda() {
+ return lambda != null && flavour != Flavour.Unclassified;
+ }
+
+ public boolean isKotlinStyleLambda() {
+ return flavour == Flavour.KotlinStyleLambda;
+ }
+
+ public boolean isJavaStyleLambda() {
+ return flavour == Flavour.JavaStyleLambda;
+ }
+
+ @Override
+ public boolean isSyntheticClass() {
+ return true;
+ }
+
+ @Override
+ public KotlinSyntheticClassInfo asSyntheticClass() {
+ return this;
+ }
+
+ @Override
+ public KotlinClassHeader rewrite(
+ DexClass clazz, AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+ Writer writer = new Writer();
+ if (lambda != null) {
+ KmLambda kmLambda = new KmLambda();
+ if (lambda.rewrite(() -> kmLambda, clazz, appView, namingLens)) {
+ kmLambda.accept(writer);
+ }
+ }
+ return writer.write().getHeader();
+ }
+
+ private static Flavour getFlavour(
+ KotlinClassMetadata.SyntheticClass metadata, DexClass clazz, Kotlin kotlin) {
+ // Returns KotlinStyleLambda if the given clazz is a Kotlin-style lambda:
+ // a class that
+ // 1) is recognized as lambda in its Kotlin metadata;
+ // 2) directly extends kotlin.jvm.internal.Lambda
+ if (metadata.isLambda() && clazz.superType == kotlin.functional.lambdaType) {
+ return Flavour.KotlinStyleLambda;
+ }
+ // Returns JavaStyleLambda if the given clazz is a Java-style lambda:
+ // a class that
+ // 1) is recognized as lambda in its Kotlin metadata;
+ // 2) doesn't extend any other class;
+ // 3) directly implements only one Java SAM.
+ if (metadata.isLambda()
+ && clazz.superType == kotlin.factory.objectType
+ && clazz.interfaces.size() == 1) {
+ return Flavour.JavaStyleLambda;
+ }
+ return Flavour.Unclassified;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
new file mode 100644
index 0000000..974469c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeAliasInfo.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.List;
+import kotlinx.metadata.KmTypeAlias;
+import kotlinx.metadata.KmTypeAliasVisitor;
+
+// Holds information about KmTypeAlias
+public class KotlinTypeAliasInfo {
+
+ private final int flags;
+ private final String name;
+ private final KotlinTypeInfo underlyingType;
+ private final KotlinTypeInfo expandedType;
+ private final List<KotlinTypeParameterInfo> typeParameters;
+ private final List<KotlinAnnotationInfo> annotations;
+
+ private KotlinTypeAliasInfo(
+ int flags,
+ String name,
+ KotlinTypeInfo underlyingType,
+ KotlinTypeInfo expandedType,
+ List<KotlinTypeParameterInfo> typeParameters,
+ List<KotlinAnnotationInfo> annotations) {
+ this.flags = flags;
+ this.name = name;
+ assert underlyingType != null;
+ assert expandedType != null;
+ this.underlyingType = underlyingType;
+ this.expandedType = expandedType;
+ this.typeParameters = typeParameters;
+ this.annotations = annotations;
+ }
+
+ public static KotlinTypeAliasInfo create(KmTypeAlias alias, AppView<?> appView) {
+ return new KotlinTypeAliasInfo(
+ alias.getFlags(),
+ alias.getName(),
+ KotlinTypeInfo.create(alias.underlyingType, appView),
+ KotlinTypeInfo.create(alias.expandedType, appView),
+ KotlinTypeParameterInfo.create(alias.getTypeParameters(), appView),
+ KotlinAnnotationInfo.create(alias.getAnnotations(), appView));
+ }
+
+ void rewrite(
+ KmVisitorProviders.KmTypeAliasVisitorProvider visitorProvider,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens namingLens) {
+ KmTypeAliasVisitor kmTypeAliasVisitor = visitorProvider.get(flags, name);
+ underlyingType.rewrite(kmTypeAliasVisitor::visitUnderlyingType, appView, namingLens);
+ expandedType.rewrite(kmTypeAliasVisitor::visitExpandedType, appView, namingLens);
+ for (KotlinTypeParameterInfo typeParameter : typeParameters) {
+ typeParameter.rewrite(kmTypeAliasVisitor::visitTypeParameter, appView, namingLens);
+ }
+ for (KotlinAnnotationInfo annotation : annotations) {
+ annotation.rewrite(kmTypeAliasVisitor::visitAnnotation, appView, namingLens);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
index 75d80b1..f00a544 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeInfo.java
@@ -4,144 +4,100 @@
package com.android.tools.r8.kotlin;
-import static kotlinx.metadata.FlagsKt.flagsOf;
-
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.kotlin.Kotlin.ClassClassifiers;
+import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.collect.ImmutableList;
import java.util.List;
-import kotlinx.metadata.KmClassifier;
import kotlinx.metadata.KmType;
import kotlinx.metadata.KmTypeProjection;
import kotlinx.metadata.KmTypeVisitor;
+import kotlinx.metadata.jvm.JvmExtensionsKt;
+import kotlinx.metadata.jvm.JvmTypeExtensionVisitor;
// Provides access to Kotlin information about a kotlin type.
public class KotlinTypeInfo {
- static final List<KotlinTypeProjectionInfo> EMPTY_ARGUMENTS = ImmutableList.of();
+ private static final List<KotlinTypeProjectionInfo> EMPTY_ARGUMENTS = ImmutableList.of();
- private final KmClassifier classifier;
+ private final int flags;
+ private final KotlinClassifierInfo classifier;
+ private final KotlinTypeInfo abbreviatedType;
+ private final KotlinTypeInfo outerType;
private final List<KotlinTypeProjectionInfo> arguments;
+ private final List<KotlinAnnotationInfo> annotations;
+ // TODO(b/154351125): Extend with flexible upper bounds
- private KotlinTypeInfo(KmClassifier classifier, List<KotlinTypeProjectionInfo> arguments) {
+ private KotlinTypeInfo(
+ int flags,
+ KotlinClassifierInfo classifier,
+ KotlinTypeInfo abbreviatedType,
+ KotlinTypeInfo outerType,
+ List<KotlinTypeProjectionInfo> arguments,
+ List<KotlinAnnotationInfo> annotations) {
+ this.flags = flags;
this.classifier = classifier;
+ this.abbreviatedType = abbreviatedType;
+ this.outerType = outerType;
this.arguments = arguments;
- }
-
- static KotlinTypeInfo create(KmType kmType) {
- if (kmType == null) {
- return null;
- }
- if (kmType.getArguments().isEmpty()) {
- return new KotlinTypeInfo(kmType.classifier, EMPTY_ARGUMENTS);
- }
- ImmutableList.Builder<KotlinTypeProjectionInfo> arguments = new ImmutableList.Builder<>();
- for (KmTypeProjection argument : kmType.getArguments()) {
- arguments.add(KotlinTypeProjectionInfo.create(argument));
- }
- return new KotlinTypeInfo(kmType.classifier, arguments.build());
- }
-
- public boolean isTypeAlias() {
- return classifier instanceof KmClassifier.TypeAlias;
- }
-
- public KmClassifier.TypeAlias asTypeAlias() {
- return (KmClassifier.TypeAlias) classifier;
- }
-
- public boolean isClass() {
- return classifier instanceof KmClassifier.Class;
- }
-
- public KmClassifier.Class asClass() {
- return (KmClassifier.Class) classifier;
- }
-
- public boolean isTypeParameter() {
- return classifier instanceof KmClassifier.TypeParameter;
- }
-
- public KmClassifier.TypeParameter asTypeParameter() {
- return (KmClassifier.TypeParameter) classifier;
- }
-
- public boolean isObjectArray() {
- if (isClass()) {
- KmClassifier.Class classifier = (KmClassifier.Class) this.classifier;
- return classifier.getName().equals(ClassClassifiers.arrayBinaryName) && arguments.size() == 1;
- }
- return false;
+ this.annotations = annotations;
}
public List<KotlinTypeProjectionInfo> getArguments() {
return arguments;
}
- public KotlinTypeProjectionInfo getArgumentOrNull(int index) {
- List<KotlinTypeProjectionInfo> arguments = getArguments();
- return arguments.size() > index ? getArguments().get(index) : null;
- }
-
- public KotlinTypeInfo toRenamed(KotlinMetadataSynthesizer synthesizer) {
- DexType originalType = getLiveDexTypeFromClassClassifier(synthesizer.appView);
- if (isClass() && originalType == null) {
+ static KotlinTypeInfo create(KmType kmType, AppView<?> appView) {
+ if (kmType == null) {
return null;
}
- KmClassifier renamedClassifier = classifier;
- if (originalType != null) {
- String typeClassifier = synthesizer.toRenamedClassifier(originalType);
- if (typeClassifier != null) {
- renamedClassifier = new KmClassifier.Class(typeClassifier);
+ return new KotlinTypeInfo(
+ kmType.getFlags(),
+ KotlinClassifierInfo.create(kmType.classifier, appView),
+ KotlinTypeInfo.create(kmType.getAbbreviatedType(), appView),
+ KotlinTypeInfo.create(kmType.getOuterType(), appView),
+ getArguments(kmType.getArguments(), appView),
+ KotlinAnnotationInfo.create(JvmExtensionsKt.getAnnotations(kmType), appView));
+ }
+
+ private static List<KotlinTypeProjectionInfo> getArguments(
+ List<KmTypeProjection> projections, AppView<?> appView) {
+ if (projections.isEmpty()) {
+ return EMPTY_ARGUMENTS;
+ }
+ ImmutableList.Builder<KotlinTypeProjectionInfo> arguments = ImmutableList.builder();
+ for (KmTypeProjection projection : projections) {
+ arguments.add(KotlinTypeProjectionInfo.create(projection, appView));
+ }
+ return arguments.build();
+ }
+
+ public void rewrite(
+ KmVisitorProviders.KmTypeVisitorProvider visitorProvider,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens namingLens) {
+ // TODO(b/154348683): Check for correct flags
+ KmTypeVisitor kmTypeVisitor = visitorProvider.get(flags);
+ classifier.rewrite(kmTypeVisitor, appView, namingLens);
+ if (abbreviatedType != null) {
+ abbreviatedType.rewrite(kmTypeVisitor::visitAbbreviatedType, appView, namingLens);
+ }
+ if (outerType != null) {
+ outerType.rewrite(kmTypeVisitor::visitOuterType, appView, namingLens);
+ }
+ for (KotlinTypeProjectionInfo argument : arguments) {
+ argument.rewrite(
+ kmTypeVisitor::visitArgument, kmTypeVisitor::visitStarProjection, appView, namingLens);
+ }
+ if (annotations.isEmpty()) {
+ return;
+ }
+ JvmTypeExtensionVisitor extensionVisitor =
+ (JvmTypeExtensionVisitor) kmTypeVisitor.visitExtensions(JvmTypeExtensionVisitor.TYPE);
+ if (extensionVisitor != null) {
+ for (KotlinAnnotationInfo annotation : annotations) {
+ annotation.rewrite(extensionVisitor::visitAnnotation, appView, namingLens);
}
}
- if (arguments.isEmpty()) {
- return renamedClassifier == classifier
- ? this
- : new KotlinTypeInfo(renamedClassifier, EMPTY_ARGUMENTS);
- }
- ImmutableList.Builder<KotlinTypeProjectionInfo> builder = ImmutableList.builder();
- for (KotlinTypeProjectionInfo argument : arguments) {
- builder.add(
- new KotlinTypeProjectionInfo(
- argument.variance,
- argument.typeInfo == null ? null : argument.typeInfo.toRenamed(synthesizer)));
- }
- return new KotlinTypeInfo(renamedClassifier, builder.build());
- }
-
- private DexType getLiveDexTypeFromClassClassifier(AppView<AppInfoWithLiveness> appView) {
- if (!isClass()) {
- return null;
- }
- String descriptor = DescriptorUtils.getDescriptorFromKotlinClassifier(asClass().getName());
- DexType type = appView.dexItemFactory().createType(descriptor);
- if (appView.appInfo().wasPruned(type)) {
- return null;
- }
- return type;
- }
-
- public KmType asKmType() {
- KmType kmType = new KmType(flagsOf());
- visit(kmType);
- return kmType;
- }
-
- public void visit(KmTypeVisitor visitor) {
- if (isClass()) {
- visitor.visitClass(asClass().getName());
- } else if (isTypeAlias()) {
- visitor.visitTypeAlias(asTypeAlias().getName());
- } else {
- assert isTypeParameter();
- visitor.visitTypeParameter(asTypeParameter().getId());
- }
- for (KotlinTypeProjectionInfo argument : arguments) {
- argument.visit(visitor);
- }
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java
index b9a86d4..e79ea83 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeParameterInfo.java
@@ -4,45 +4,96 @@
package com.android.tools.r8.kotlin;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import kotlinx.metadata.KmType;
import kotlinx.metadata.KmTypeParameter;
+import kotlinx.metadata.KmTypeParameterVisitor;
import kotlinx.metadata.KmVariance;
+import kotlinx.metadata.jvm.JvmExtensionsKt;
+import kotlinx.metadata.jvm.JvmTypeParameterExtensionVisitor;
// Provides access to Kotlin information about a type-parameter.
public class KotlinTypeParameterInfo {
+ private static final List<KotlinTypeParameterInfo> EMPTY_TYPE_PARAMETERS = ImmutableList.of();
+ private static final List<KotlinTypeInfo> EMPTY_UPPER_BOUNDS = ImmutableList.of();
+
private final int flags;
private final int id;
private final String name;
private final KmVariance variance;
+ private final List<KotlinTypeInfo> originalUpperBounds;
+ private final List<KotlinAnnotationInfo> annotations;
- private KotlinTypeParameterInfo(int flags, int id, String name, KmVariance variance) {
+ private KotlinTypeParameterInfo(
+ int flags,
+ int id,
+ String name,
+ KmVariance variance,
+ List<KotlinTypeInfo> originalUpperBounds,
+ List<KotlinAnnotationInfo> annotations) {
this.flags = flags;
this.id = id;
this.name = name;
this.variance = variance;
+ this.originalUpperBounds = originalUpperBounds;
+ this.annotations = annotations;
}
- static KotlinTypeParameterInfo fromKmTypeParameter(KmTypeParameter kmTypeParameter) {
+ private static KotlinTypeParameterInfo create(
+ KmTypeParameter kmTypeParameter, AppView<?> appView) {
return new KotlinTypeParameterInfo(
kmTypeParameter.getFlags(),
kmTypeParameter.getId(),
kmTypeParameter.getName(),
- kmTypeParameter.getVariance());
+ kmTypeParameter.getVariance(),
+ getUpperBounds(kmTypeParameter.getUpperBounds(), appView),
+ KotlinAnnotationInfo.create(JvmExtensionsKt.getAnnotations(kmTypeParameter), appView));
}
- public int getFlags() {
- return flags;
+ static List<KotlinTypeParameterInfo> create(
+ List<KmTypeParameter> kmTypeParameters, AppView<?> appView) {
+ if (kmTypeParameters.isEmpty()) {
+ return EMPTY_TYPE_PARAMETERS;
+ }
+ ImmutableList.Builder<KotlinTypeParameterInfo> builder = ImmutableList.builder();
+ for (KmTypeParameter kmTypeParameter : kmTypeParameters) {
+ builder.add(create(kmTypeParameter, appView));
+ }
+ return builder.build();
}
- public int getId() {
- return id;
+ private static List<KotlinTypeInfo> getUpperBounds(List<KmType> upperBounds, AppView<?> appView) {
+ if (upperBounds.isEmpty()) {
+ return EMPTY_UPPER_BOUNDS;
+ }
+ ImmutableList.Builder<KotlinTypeInfo> builder = ImmutableList.builder();
+ for (KmType upperBound : upperBounds) {
+ builder.add(KotlinTypeInfo.create(upperBound, appView));
+ }
+ return builder.build();
}
- public String getName() {
- return name;
- }
-
- public KmVariance getVariance() {
- return variance;
+ void rewrite(
+ KmVisitorProviders.KmTypeParameterVisitorProvider visitorProvider,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens namingLens) {
+ KmTypeParameterVisitor kmTypeParameterVisitor = visitorProvider.get(flags, name, id, variance);
+ for (KotlinTypeInfo originalUpperBound : originalUpperBounds) {
+ originalUpperBound.rewrite(kmTypeParameterVisitor::visitUpperBound, appView, namingLens);
+ }
+ if (annotations.isEmpty()) {
+ return;
+ }
+ JvmTypeParameterExtensionVisitor extensionVisitor =
+ (JvmTypeParameterExtensionVisitor)
+ kmTypeParameterVisitor.visitExtensions(JvmTypeParameterExtensionVisitor.TYPE);
+ for (KotlinAnnotationInfo annotation : annotations) {
+ annotation.rewrite(extensionVisitor::visitAnnotation, appView, namingLens);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
index a16c78e..9ab8adc 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinTypeProjectionInfo.java
@@ -4,10 +4,10 @@
package com.android.tools.r8.kotlin;
-import static kotlinx.metadata.FlagsKt.flagsOf;
-
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import kotlinx.metadata.KmTypeProjection;
-import kotlinx.metadata.KmTypeVisitor;
import kotlinx.metadata.KmVariance;
// Provides access to Kotlin information about the type projection of a type (arguments).
@@ -16,27 +16,29 @@
final KmVariance variance;
final KotlinTypeInfo typeInfo;
- KotlinTypeProjectionInfo(KmVariance variance, KotlinTypeInfo typeInfo) {
+ private KotlinTypeProjectionInfo(KmVariance variance, KotlinTypeInfo typeInfo) {
this.variance = variance;
this.typeInfo = typeInfo;
}
- static KotlinTypeProjectionInfo create(KmTypeProjection kmTypeProjection) {
+ static KotlinTypeProjectionInfo create(KmTypeProjection kmTypeProjection, AppView<?> appView) {
return new KotlinTypeProjectionInfo(
- kmTypeProjection.getVariance(), KotlinTypeInfo.create(kmTypeProjection.getType()));
+ kmTypeProjection.getVariance(), KotlinTypeInfo.create(kmTypeProjection.getType(), appView));
}
- public boolean isStarProjection() {
+ private boolean isStarProjection() {
return variance == null && typeInfo == null;
}
- public void visit(KmTypeVisitor visitor) {
- KmTypeVisitor kmTypeVisitor = visitor.visitArgument(flagsOf(), variance);
- // TODO(b/152886451): Check if this check should be before visitor.visitArgument(...).
+ public void rewrite(
+ KmVisitorProviders.KmTypeProjectionVisitorProvider visitorProvider,
+ KmVisitorProviders.KmTypeStarProjectionVisitorProvider starProjectionProvider,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens namingLens) {
if (isStarProjection()) {
- kmTypeVisitor.visitStarProjection();
+ starProjectionProvider.get();
} else {
- typeInfo.visit(kmTypeVisitor);
+ typeInfo.rewrite(flags -> visitorProvider.get(flags, variance), appView, namingLens);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
index e358c50..d092495 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinValueParameterInfo.java
@@ -1,54 +1,71 @@
// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+
package com.android.tools.r8.kotlin;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableList;
import java.util.List;
-import kotlinx.metadata.KmAnnotation;
import kotlinx.metadata.KmType;
import kotlinx.metadata.KmValueParameter;
-import kotlinx.metadata.jvm.JvmExtensionsKt;
+import kotlinx.metadata.KmValueParameterVisitor;
// Provides access to Kotlin information about value parameter.
class KotlinValueParameterInfo {
- // TODO(b/151193860): When to use original param name v.s. when to *not* use?
+ private static final List<KotlinValueParameterInfo> EMPTY_VALUE_PARAMETERS = ImmutableList.of();
// Original parameter name.
final String name;
- // Original parameter flag, e.g., has default value.
- final int flag;
- // Indicates whether the formal parameter is originally `vararg`.
- final boolean isVararg;
+ // Original parameter flags, e.g., has default value.
+ final int flags;
// Original information about the type.
final KotlinTypeInfo type;
-
- // TODO(b/151194869): Should we treat them as normal annotations? E.g., shrinking and renaming?
- // Annotations on the type of value parameter.
- final List<KmAnnotation> annotations;
+ // Indicates whether the formal parameter is originally `vararg`.
+ final KotlinTypeInfo varargElementType;
private KotlinValueParameterInfo(
- String name,
- int flag,
- boolean isVararg,
- KotlinTypeInfo type,
- List<KmAnnotation> annotations) {
+ int flags, String name, KotlinTypeInfo type, KotlinTypeInfo varargElementType) {
this.name = name;
- this.flag = flag;
- this.isVararg = isVararg;
+ this.flags = flags;
this.type = type;
- this.annotations = annotations;
+ this.varargElementType = varargElementType;
}
- static KotlinValueParameterInfo fromKmValueParameter(KmValueParameter kmValueParameter) {
+ static KotlinValueParameterInfo create(KmValueParameter kmValueParameter, AppView<?> appView) {
if (kmValueParameter == null) {
return null;
}
KmType kmType = kmValueParameter.getType();
return new KotlinValueParameterInfo(
- kmValueParameter.getName(),
kmValueParameter.getFlags(),
- kmValueParameter.getVarargElementType() != null,
- KotlinTypeInfo.create(kmType),
- kmType != null ? JvmExtensionsKt.getAnnotations(kmType) : ImmutableList.of());
+ kmValueParameter.getName(),
+ KotlinTypeInfo.create(kmType, appView),
+ KotlinTypeInfo.create(kmValueParameter.getVarargElementType(), appView));
+ }
+
+ static List<KotlinValueParameterInfo> create(
+ List<KmValueParameter> parameters, AppView<?> appView) {
+ if (parameters.isEmpty()) {
+ return EMPTY_VALUE_PARAMETERS;
+ }
+ ImmutableList.Builder<KotlinValueParameterInfo> builder = ImmutableList.builder();
+ for (KmValueParameter parameter : parameters) {
+ builder.add(create(parameter, appView));
+ }
+ return builder.build();
+ }
+
+ void rewrite(
+ KmVisitorProviders.KmValueParameterVisitorProvider visitorProvider,
+ AppView<AppInfoWithLiveness> appView,
+ NamingLens namingLens) {
+ KmValueParameterVisitor kmValueParameterVisitor = visitorProvider.get(flags, name);
+ type.rewrite(kmValueParameterVisitor::visitType, appView, namingLens);
+ if (varargElementType != null) {
+ varargElementType.rewrite(
+ kmValueParameterVisitor::visitVarargElementType, appView, namingLens);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
index 0aeae73..e98d9b9 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.BiMapContainer;
+import com.android.tools.r8.utils.ChainableStringConsumer;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap;
@@ -23,16 +24,14 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.io.StringWriter;
-import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
-import java.util.List;
+import java.util.Iterator;
import java.util.Map;
+import java.util.Map.Entry;
public class ClassNameMapper implements ProguardMap {
@@ -113,6 +112,14 @@
this.classNameMappings = builder.build();
}
+ private ClassNameMapper(ImmutableMap<String, ClassNamingForNameMapper> classNameMappings) {
+ this.classNameMappings = classNameMappings;
+ }
+
+ public Map<String, ClassNamingForNameMapper> getClassNameMappings() {
+ return classNameMappings;
+ }
+
private Signature canonicalizeSignature(Signature signature) {
Signature result = signatureMap.get(signature);
if (result != null) {
@@ -174,26 +181,45 @@
return classNameMappings.get(obfuscatedName);
}
- public void write(Writer writer) throws IOException {
- // Sort classes by their original name such that the generated Proguard map is deterministic
- // (and easy to navigate manually).
- List<ClassNamingForNameMapper> classNamingForNameMappers =
- new ArrayList<>(classNameMappings.values());
- classNamingForNameMappers.sort(Comparator.comparing(x -> x.originalName));
- for (ClassNamingForNameMapper naming : classNamingForNameMappers) {
- naming.write(writer);
+ public boolean isEmpty() {
+ return classNameMappings.isEmpty();
+ }
+
+ public ClassNameMapper sorted() {
+ ImmutableMap.Builder<String, ClassNamingForNameMapper> builder = ImmutableMap.builder();
+ builder.orderEntriesByValue(Comparator.comparing(x -> x.originalName));
+ classNameMappings.forEach(builder::put);
+ return new ClassNameMapper(builder.build());
+ }
+
+ public boolean verifyIsSorted() {
+ Iterator<Entry<String, ClassNamingForNameMapper>> iterator =
+ getClassNameMappings().entrySet().iterator();
+ Iterator<Entry<String, ClassNamingForNameMapper>> sortedIterator =
+ sorted().getClassNameMappings().entrySet().iterator();
+ while (iterator.hasNext()) {
+ Entry<String, ClassNamingForNameMapper> entry = iterator.next();
+ Entry<String, ClassNamingForNameMapper> otherEntry = sortedIterator.next();
+ assert entry.getKey().equals(otherEntry.getKey());
+ assert entry.getValue() == otherEntry.getValue();
+ }
+ return true;
+ }
+
+ public void write(ChainableStringConsumer consumer) {
+ // Classes should be sorted by their original name such that the generated Proguard map is
+ // deterministic (and easy to navigate manually).
+ assert verifyIsSorted();
+ for (ClassNamingForNameMapper naming : getClassNameMappings().values()) {
+ naming.write(consumer);
}
}
@Override
public String toString() {
- try {
- StringWriter writer = new StringWriter();
- write(writer);
- return writer.toString();
- } catch (IOException e) {
- return e.toString();
- }
+ StringBuilder builder = new StringBuilder();
+ write(ChainableStringConsumer.wrap(builder::append));
+ return builder.toString();
}
public BiMapContainer<String, String> getObfuscatedToOriginalMapping() {
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index be788ab..d7797a4 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -48,13 +48,14 @@
private final boolean isAccessModificationAllowed;
private final Set<String> noObfuscationPrefixes = Sets.newHashSet();
private final Set<String> usedPackagePrefixes = Sets.newHashSet();
- private final Set<DexString> usedTypeNames = Sets.newIdentityHashSet();
-
+ private final Set<String> usedTypeNames = Sets.newHashSet();
private final Map<DexType, DexString> renaming = Maps.newIdentityHashMap();
private final Map<String, Namespace> states = new HashMap<>();
private final boolean keepInnerClassStructure;
private final Namespace topLevelState;
+ private final boolean allowMixedCaseNaming;
+ private final Predicate<String> isUsed;
ClassNameMinifier(
AppView<AppInfoWithLiveness> appView,
@@ -76,11 +77,23 @@
getPackageBinaryNameFromJavaType(options.getProguardConfiguration().getPackagePrefix()));
states.put("", topLevelState);
+
+ if (options.getProguardConfiguration().hasDontUseMixedCaseClassnames()) {
+ allowMixedCaseNaming = false;
+ isUsed = candidate -> usedTypeNames.contains(candidate.toLowerCase());
+ } else {
+ allowMixedCaseNaming = true;
+ isUsed = usedTypeNames::contains;
+ }
+ }
+
+ private void setUsedTypeName(String typeName) {
+ usedTypeNames.add(allowMixedCaseNaming ? typeName : typeName.toLowerCase());
}
static class ClassRenaming {
- protected final Map<String, String> packageRenaming;
- protected final Map<DexType, DexString> classRenaming;
+ final Map<String, String> packageRenaming;
+ final Map<DexType, DexString> classRenaming;
private ClassRenaming(
Map<DexType, DexString> classRenaming, Map<String, String> packageRenaming) {
@@ -195,7 +208,7 @@
renaming.put(type, descriptor);
registerPackagePrefixesAsUsed(
getParentPackagePrefix(getClassBinaryNameFromDescriptor(descriptor.toSourceString())));
- usedTypeNames.add(descriptor);
+ setUsedTypeName(descriptor.toString());
if (keepInnerClassStructure) {
DexType outerClass = getOutClassForType(type);
if (outerClass != null) {
@@ -387,10 +400,9 @@
}
DexString nextTypeName(DexType type) {
- DexString candidate =
- classNamingStrategy.next(type, packagePrefix, this, usedTypeNames::contains);
- assert !usedTypeNames.contains(candidate);
- usedTypeNames.add(candidate);
+ DexString candidate = classNamingStrategy.next(type, packagePrefix, this, isUsed);
+ assert !usedTypeNames.contains(candidate.toString());
+ setUsedTypeName(candidate.toString());
return candidate;
}
@@ -420,7 +432,7 @@
protected interface ClassNamingStrategy {
DexString next(
- DexType type, char[] packagePrefix, InternalNamingState state, Predicate<DexString> isUsed);
+ DexType type, char[] packagePrefix, InternalNamingState state, Predicate<String> isUsed);
/**
* Returns the reserved descriptor for a type. If the type is not allowed to be obfuscated
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
index fd5df23..5a00fcb 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -7,12 +7,10 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.naming.MemberNaming.Signature.SignatureKind;
+import com.android.tools.r8.utils.ChainableStringConsumer;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -184,7 +182,7 @@
}
public final String originalName;
- private final String renamedName;
+ public final String renamedName;
/**
* Mapping from the renamed signature to the naming information for a member.
@@ -297,19 +295,11 @@
return methodMembers.values();
}
- void write(Writer writer) throws IOException {
- writer.append(originalName);
- writer.append(" -> ");
- writer.append(renamedName);
- writer.append(":\n");
+ void write(ChainableStringConsumer consumer) {
+ consumer.accept(originalName).accept(" -> ").accept(renamedName).accept(":\n");
- // First print non-method MemberNamings.
- forAllMemberNaming(
- m -> {
- if (!m.isMethodNaming()) {
- writer.append(" ").append(m.toString()).append('\n');
- }
- });
+ // First print field member namings.
+ forAllFieldNaming(m -> consumer.accept(" ").accept(m.toString()).accept("\n"));
// Sort MappedRanges by sequence number to restore construction order (original Proguard-map
// input).
@@ -319,19 +309,15 @@
}
mappedRangesSorted.sort(Comparator.comparingInt(range -> range.sequenceNumber));
for (MappedRange range : mappedRangesSorted) {
- writer.append(" ").append(range.toString()).append('\n');
+ consumer.accept(" ").accept(range.toString()).accept("\n");
}
}
@Override
public String toString() {
- try {
- StringWriter writer = new StringWriter();
- write(writer);
- return writer.toString();
- } catch (IOException e) {
- return e.toString();
- }
+ StringBuilder builder = new StringBuilder();
+ write(ChainableStringConsumer.wrap(builder::append));
+ return builder.toString();
}
@Override
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 59fee69..f789fe4 100644
--- a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
@@ -141,13 +141,13 @@
}
@Override
- void forAllRenamedTypes(Consumer<DexType> consumer) {
+ public void forAllRenamedTypes(Consumer<DexType> consumer) {
DexReference.filterDexType(DexReference.filterDexReference(renaming.keySet().stream()))
.forEach(consumer);
}
@Override
- <T extends DexItem> Map<String, T> getRenamedItems(
+ public <T extends DexItem> Map<String, T> getRenamedItems(
Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) {
return renaming.keySet().stream()
.filter(item -> (clazz.isInstance(item) && predicate.test(clazz.cast(item))))
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index d83a0f0..8f1a41a 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -148,11 +148,8 @@
@Override
public DexString next(
- DexType type,
- char[] packagePrefix,
- InternalNamingState state,
- Predicate<DexString> isUsed) {
- DexString candidate = null;
+ DexType type, char[] packagePrefix, InternalNamingState state, Predicate<String> isUsed) {
+ String candidate = null;
String lastName = null;
do {
String newName = nextName(packagePrefix, state, false) + ";";
@@ -170,9 +167,9 @@
if (newName.endsWith("LR;") || newName.endsWith("/R;")) {
continue;
}
- candidate = factory.createString(newName);
+ candidate = newName;
} while (candidate == null || isUsed.test(candidate));
- return candidate;
+ return factory.createString(candidate);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/naming/NamingLens.java b/src/main/java/com/android/tools/r8/naming/NamingLens.java
index b3694f7..a142cbf 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingLens.java
@@ -124,9 +124,9 @@
return DescriptorUtils.descriptorToInternalName(lookupDescriptor(type).toString());
}
- abstract void forAllRenamedTypes(Consumer<DexType> consumer);
+ public abstract void forAllRenamedTypes(Consumer<DexType> consumer);
- abstract <T extends DexItem> Map<String, T> getRenamedItems(
+ public abstract <T extends DexItem> Map<String, T> getRenamedItems(
Class<T> clazz, Predicate<T> predicate, Function<T, String> namer);
/**
@@ -211,12 +211,12 @@
}
@Override
- void forAllRenamedTypes(Consumer<DexType> consumer) {
+ public void forAllRenamedTypes(Consumer<DexType> consumer) {
// Intentionally left empty.
}
@Override
- <T extends DexItem> Map<String, T> getRenamedItems(
+ public <T extends DexItem> Map<String, T> getRenamedItems(
Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) {
return ImmutableMap.of();
}
diff --git a/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java b/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
index e411211..482750f 100644
--- a/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
@@ -96,7 +96,8 @@
@Override
public DexString lookupMethodName(DexCallSite callSite) {
- if (isRenamed(callSite.bootstrapMethod.rewrittenTarget.holder)) {
+ if (callSite.bootstrapMethod.rewrittenTarget != null
+ && isRenamed(callSite.bootstrapMethod.rewrittenTarget.holder)) {
// Prefix rewriting does not influence the inner name.
return callSite.methodName;
}
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 9a30e79..5e23d98 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -405,10 +405,7 @@
@Override
public DexString next(
- DexType type,
- char[] packagePrefix,
- InternalNamingState state,
- Predicate<DexString> isUsed) {
+ DexType type, char[] packagePrefix, InternalNamingState state, Predicate<String> isUsed) {
assert !mappings.containsKey(type);
assert appView.rootSet().mayBeMinified(type, appView);
return super.next(
@@ -416,7 +413,7 @@
packagePrefix,
state,
candidate -> {
- if (mappedNames.contains(candidate.toString())) {
+ if (mappedNames.contains(candidate)) {
return true;
}
return isUsed.test(candidate);
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
index 0cee87f..ec947e8 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
@@ -3,16 +3,19 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.Version;
-import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.ChainableStringConsumer;
+import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.VersionProperties;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.Writer;
public class ProguardMapSupplier {
@@ -25,57 +28,57 @@
public static int PG_MAP_ID_LENGTH = 7;
- public static ProguardMapSupplier fromClassNameMapper(
- ClassNameMapper classNameMapper, InternalOptions options) {
- return new ProguardMapSupplier(true, classNameMapper, null, null, options);
- }
-
- public static ProguardMapSupplier fromNamingLens(
- NamingLens namingLens, DexApplication dexApplication, InternalOptions options) {
- return new ProguardMapSupplier(false, null, namingLens, dexApplication, options);
- }
-
- public static class ProguardMapAndId {
- public final String map;
- public final String id;
-
- ProguardMapAndId(String map, String id) {
- assert map != null && id != null;
- this.map = map;
- this.id = id;
+ // Truncated murmur hash of the non-whitespace codepoints of the Proguard map (excluding the
+ // marker).
+ public static class ProguardMapId extends Box<String> {
+ private ProguardMapId(String id) {
+ super(id);
+ assert id != null;
+ assert id.length() == PG_MAP_ID_LENGTH;
}
}
- public ProguardMapSupplier(
- boolean useClassNameMapper,
- ClassNameMapper classNameMapper,
- NamingLens namingLens,
- DexApplication application,
- InternalOptions options) {
- this.useClassNameMapper = useClassNameMapper;
- this.classNameMapper = classNameMapper;
- this.namingLens = namingLens;
- this.application = application;
- this.minApiLevel = options.isGeneratingClassFiles() ? null : options.minApiLevel;
- }
-
- private final boolean useClassNameMapper;
private final ClassNameMapper classNameMapper;
- private final NamingLens namingLens;
- private final DexApplication application;
- private final Integer minApiLevel;
+ private final StringConsumer consumer;
+ private final InternalOptions options;
+ private final Reporter reporter;
- public ProguardMapAndId getProguardMapAndId() {
- String body = getBody();
- if (body == null || body.trim().length() == 0) {
- return null;
- }
- // Algorithm:
- // Hash of the non-whitespace codepoints of the input string.
- Hasher hasher = Hashing.murmur3_32().newHasher();
- body.codePoints().filter(c -> !Character.isWhitespace(c)).forEach(hasher::putInt);
- String proguardMapId = hasher.hash().toString().substring(0, PG_MAP_ID_LENGTH);
+ private ProguardMapSupplier(ClassNameMapper classNameMapper, InternalOptions options) {
+ assert classNameMapper != null;
+ assert !classNameMapper.isEmpty();
+ this.classNameMapper = classNameMapper.sorted();
+ this.consumer =
+ InternalOptions.assertionsEnabled()
+ ? new ProguardMapChecker(options.proguardMapConsumer)
+ : options.proguardMapConsumer;
+ this.options = options;
+ this.reporter = options.reporter;
+ }
+ public static ProguardMapSupplier create(
+ ClassNameMapper classNameMapper, InternalOptions options) {
+ return classNameMapper.isEmpty() ? null : new ProguardMapSupplier(classNameMapper, options);
+ }
+
+ public ProguardMapId writeProguardMap() {
+ ProguardMapId id = computeProguardMapId();
+ writeMarker(id);
+ writeBody();
+ ExceptionUtils.withFinishedResourceHandler(reporter, consumer);
+ return id;
+ }
+
+ private ProguardMapId computeProguardMapId() {
+ ProguardMapIdBuilder builder = new ProguardMapIdBuilder();
+ classNameMapper.write(builder);
+ return builder.build();
+ }
+
+ private void writeBody() {
+ classNameMapper.write(new ProguardMapWriter());
+ }
+
+ private void writeMarker(ProguardMapId id) {
StringBuilder builder = new StringBuilder();
builder.append(
"# "
@@ -88,44 +91,81 @@
+ ": "
+ Version.LABEL
+ "\n");
- if (minApiLevel != null) {
- builder.append("# " + MARKER_KEY_MIN_API + ": " + minApiLevel + "\n");
+ if (options.isGeneratingDex()) {
+ builder.append("# " + MARKER_KEY_MIN_API + ": " + options.minApiLevel + "\n");
}
if (Version.isDevelopmentVersion()) {
builder.append(
"# " + MARKER_KEY_COMPILER_HASH + ": " + VersionProperties.INSTANCE.getSha() + "\n");
}
- builder.append("# " + MARKER_KEY_PG_MAP_ID + ": " + proguardMapId + "\n");
+ builder.append("# " + MARKER_KEY_PG_MAP_ID + ": " + id.get() + "\n");
// Turn off linting of the mapping file in some build systems.
builder.append("# common_typos_disable" + "\n");
- builder.append(body);
-
- return new ProguardMapAndId(builder.toString(), proguardMapId);
+ consumer.accept(builder.toString(), reporter);
}
- private String getBody() {
- if (useClassNameMapper) {
- assert classNameMapper != null;
- return classNameMapper.toString();
- }
- assert namingLens != null && application != null;
- // TODO(herhut): Should writing of the proguard-map file be split like this?
- if (!namingLens.isIdentityLens()) {
- StringBuilder map = new StringBuilder();
- new MinifiedNameMapPrinter(application, namingLens).write(map);
- return map.toString();
- }
- if (application.getProguardMap() != null) {
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- Writer writer = new PrintWriter(bytes);
- try {
- application.getProguardMap().write(writer);
- writer.flush();
- } catch (IOException e) {
- throw new RuntimeException("IOException while creating Proguard-map output: " + e);
+ static class ProguardMapIdBuilder implements ChainableStringConsumer {
+
+ private final Hasher hasher = Hashing.murmur3_32().newHasher();
+
+ @Override
+ public ProguardMapIdBuilder accept(String string) {
+ for (int i = 0; i < string.length(); i++) {
+ char c = string.charAt(i);
+ if (!Character.isWhitespace(c)) {
+ hasher.putInt(c);
+ }
}
- return bytes.toString();
+ return this;
}
- return null;
+
+ public ProguardMapId build() {
+ return new ProguardMapId(hasher.hash().toString().substring(0, PG_MAP_ID_LENGTH));
+ }
+ }
+
+ class ProguardMapWriter implements ChainableStringConsumer {
+
+ @Override
+ public ProguardMapWriter accept(String string) {
+ consumer.accept(string, reporter);
+ return this;
+ }
+ }
+
+ static class ProguardMapChecker implements StringConsumer {
+
+ private final StringConsumer inner;
+ private final StringBuilder contents = new StringBuilder();
+
+ ProguardMapChecker(StringConsumer inner) {
+ if (!InternalOptions.assertionsEnabled()) {
+ // Make sure we never get here without assertions enabled.
+ throw new Unreachable();
+ }
+ this.inner = inner;
+ }
+
+ @Override
+ public void accept(String string, DiagnosticsHandler handler) {
+ inner.accept(string, handler);
+ contents.append(string);
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ inner.finished(handler);
+ assert validateProguardMapParses(contents.toString());
+ }
+
+ private static boolean validateProguardMapParses(String content) {
+ try {
+ ClassNameMapper.mapperFromString(content);
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+ return true;
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/BridgeHoisting.java b/src/main/java/com/android/tools/r8/optimize/BridgeHoisting.java
index d4a5b7e..8e5c3c3 100644
--- a/src/main/java/com/android/tools/r8/optimize/BridgeHoisting.java
+++ b/src/main/java/com/android/tools/r8/optimize/BridgeHoisting.java
@@ -202,8 +202,8 @@
// Move the bridge method to the super class, and record this in the graph lens.
DexMethod newMethod =
appView.dexItemFactory().createMethod(clazz.type, method.proto, method.name);
- clazz.addVirtualMethod(representative.getMethod().toTypeSubstitutedMethod(newMethod));
- lensBuilder.move(representative.getMethod().method, newMethod);
+ clazz.addVirtualMethod(representative.getDefinition().toTypeSubstitutedMethod(newMethod));
+ lensBuilder.move(representative.getDefinition().method, newMethod);
// Remove all of the bridges in the subclasses.
for (DexProgramClass subclass : subclasses) {
@@ -227,7 +227,7 @@
if (clazz.type.isSamePackage(representative.getHolder().type)) {
return false;
}
- return !representative.getMethod().isPublic();
+ return !representative.getDefinition().isPublic();
}
static class BridgeHoistingLens extends NestedGraphLense {
diff --git a/src/main/java/com/android/tools/r8/references/PackageReference.java b/src/main/java/com/android/tools/r8/references/PackageReference.java
new file mode 100644
index 0000000..1c54921
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/references/PackageReference.java
@@ -0,0 +1,47 @@
+// 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.references;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.util.Objects;
+
+/** Reference to a package. */
+@Keep
+public class PackageReference {
+
+ private final String packageName;
+
+ PackageReference(String packageName) {
+ if (packageName == null) {
+ throw new IllegalArgumentException("Package name cannot be null.");
+ }
+ if (!packageName.isEmpty() && !DescriptorUtils.isValidJavaType(packageName)) {
+ throw new IllegalArgumentException("Package name '" + packageName + "' is not valid.");
+ }
+ this.packageName = packageName;
+ }
+
+ public String getPackageName() {
+ return packageName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ PackageReference that = (PackageReference) o;
+ return packageName.equals(that.packageName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(packageName);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/references/Reference.java b/src/main/java/com/android/tools/r8/references/Reference.java
index dcf8c68..1071cbd 100644
--- a/src/main/java/com/android/tools/r8/references/Reference.java
+++ b/src/main/java/com/android/tools/r8/references/Reference.java
@@ -22,7 +22,7 @@
* that allocation is reduced and equality is constant time. Internally, the objects are weakly
* stored to avoid memory pressure.
*
- * <p>All reference objects are immutable and can be compared for equality using physical identity.
+ * <p>No guarantees are made on identity and all references must be compared by {@code equals}.
*/
@Keep
public final class Reference {
@@ -201,4 +201,15 @@
Class<?> fieldType = field.getType();
return field(classFromClass(holderClass), fieldName, typeFromClass(fieldType));
}
+
+ /** Create a package reference from a string */
+ public static PackageReference packageFromString(String packageName) {
+ // Note, we rely on equality check for packages and do not canonicalize them.
+ return new PackageReference(packageName);
+ }
+
+ /** Create a package from a java.lang.Package */
+ public static PackageReference packageFromPackage(Package pkg) {
+ return new PackageReference(pkg.getName());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/relocator/Relocator.java b/src/main/java/com/android/tools/r8/relocator/Relocator.java
new file mode 100644
index 0000000..a4caad6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/relocator/Relocator.java
@@ -0,0 +1,114 @@
+// 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.relocator;
+
+import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+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.GraphLense;
+import com.android.tools.r8.jar.CfApplicationWriter;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.naming.signature.GenericSignatureRewriter;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.ExceptionUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+@Keep
+public class Relocator {
+
+ private Relocator() {}
+
+ /**
+ * Main API entry for Relocator.
+ *
+ * @param command Relocator command.
+ */
+ public static void run(RelocatorCommand command) throws CompilationFailedException {
+ AndroidApp app = command.getApp();
+ InternalOptions options = command.getInternalOptions();
+ ExecutorService executor = ThreadUtils.getExecutorService(options);
+ ExceptionUtils.withCompilationHandler(
+ command.getReporter(),
+ () -> {
+ try {
+ run(command, executor, app, options);
+ } finally {
+ executor.shutdown();
+ }
+ });
+ }
+
+ /**
+ * Main API entry for Relocator with a externally supplied executor service.
+ *
+ * @param command Relocator command.
+ * @param executor executor service from which to get threads for multi-threaded processing.
+ */
+ public static void run(RelocatorCommand command, ExecutorService executor)
+ throws CompilationFailedException {
+ AndroidApp app = command.getApp();
+ InternalOptions options = command.getInternalOptions();
+ ExceptionUtils.withCompilationHandler(
+ command.getReporter(),
+ () -> {
+ run(command, executor, app, options);
+ });
+ }
+
+ private static void run(
+ RelocatorCommand command,
+ ExecutorService executor,
+ AndroidApp inputApp,
+ InternalOptions options)
+ throws IOException {
+ Timing timing = Timing.create("Relocator", options);
+ try {
+ DexApplication app = new ApplicationReader(inputApp, options, timing).read(executor);
+
+ AppInfo appInfo = new AppInfoWithClassHierarchy(app);
+ AppView<?> appView = AppView.createForRelocator(appInfo, options);
+ appView.setAppServices(AppServices.builder(appView).build());
+
+ SimplePackagesRewritingMapper packageRemapper = new SimplePackagesRewritingMapper(appView);
+ NamingLens namingLens = packageRemapper.compute(command.getMapping());
+
+ new GenericSignatureRewriter(appView, packageRemapper.getTypeMappings())
+ .run(appInfo.classes(), executor);
+
+ new CfApplicationWriter(
+ app,
+ appView,
+ options,
+ new Marker(Tool.Relocator),
+ GraphLense.getIdentityLense(),
+ namingLens,
+ null)
+ .write(command.getConsumer());
+ options.printWarnings();
+ } catch (ExecutionException e) {
+ throw unwrapExecutionException(e);
+ } finally {
+ options.signalFinishedToConsumers();
+ // Dump timings.
+ if (options.printTimes) {
+ timing.report();
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/relocator/RelocatorCommand.java b/src/main/java/com/android/tools/r8/relocator/RelocatorCommand.java
new file mode 100644
index 0000000..e329c4d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/relocator/RelocatorCommand.java
@@ -0,0 +1,421 @@
+// 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.relocator;
+
+import com.android.tools.r8.BaseCompilerCommandParser;
+import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.references.PackageReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.shaking.ProguardPathList;
+import com.android.tools.r8.utils.AbortException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
+import com.android.tools.r8.utils.FlagFile;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+@Keep
+public class RelocatorCommand {
+
+ private static final String THREAD_COUNT_FLAG = "--thread-count";
+
+ private static final Set<String> OPTIONS_WITH_PARAMETER =
+ ImmutableSet.of("--output", "--input", "--map", THREAD_COUNT_FLAG);
+
+ static final String USAGE_MESSAGE =
+ String.join(
+ "\n",
+ Iterables.concat(
+ Arrays.asList(
+ "The Relocator CLI is EXPERIMENTAL and is subject to change",
+ "Usage: relocator [options]",
+ " where options are:",
+ " --input <file> # Input file to remap, class, zip or jar.",
+ " --output <file> # Output result in <outfile>.",
+ " --map <from->to> # Registers a mapping.",
+ " --thread-count <number> # A specified number of threads to run with.",
+ " --version # Print the version of d8.",
+ " --help # Print this message.")));
+
+ private final boolean printHelp;
+ private final boolean printVersion;
+ private final Reporter reporter;
+ private final DexItemFactory factory;
+ private final ClassFileConsumer consumer;
+ private final AndroidApp app;
+ private final ImmutableMap<PackageReference, PackageReference> mapping;
+ private final int threadCount;
+
+ private RelocatorCommand(boolean printHelp, boolean printVersion) {
+ this.printHelp = printHelp;
+ this.printVersion = printVersion;
+ reporter = null;
+ factory = null;
+ consumer = null;
+ app = null;
+ mapping = null;
+ threadCount = ThreadUtils.NOT_SPECIFIED;
+ }
+
+ private RelocatorCommand(
+ ImmutableMap<PackageReference, PackageReference> mapping,
+ AndroidApp app,
+ Reporter reporter,
+ DexItemFactory factory,
+ ClassFileConsumer consumer,
+ int threadCount) {
+ this.printHelp = false;
+ this.printVersion = false;
+ this.mapping = mapping;
+ this.app = app;
+ this.reporter = reporter;
+ this.factory = factory;
+ this.consumer = consumer;
+ this.threadCount = threadCount;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static Builder parse(String[] args, Origin origin) {
+ return Builder.parse(args, origin);
+ }
+
+ public static Builder builder(DiagnosticsHandler diagnosticsHandler) {
+ return new Builder(diagnosticsHandler);
+ }
+
+ public Reporter getReporter() {
+ return reporter;
+ }
+
+ public DexItemFactory getFactory() {
+ return factory;
+ }
+
+ public ClassFileConsumer getConsumer() {
+ return consumer;
+ }
+
+ public int getThreadCount() {
+ return threadCount;
+ }
+
+ public AndroidApp getApp() {
+ return app;
+ }
+
+ public boolean isPrintHelp() {
+ return printHelp;
+ }
+
+ public boolean isPrintVersion() {
+ return printVersion;
+ }
+
+ public InternalOptions getInternalOptions() {
+ // We are using the proguard configuration for adapting resources.
+ InternalOptions options =
+ new InternalOptions(
+ ProguardConfiguration.builder(factory, getReporter())
+ .addKeepAttributePatterns(ImmutableList.of("*"))
+ .addAdaptResourceFilenames(ProguardPathList.builder().addFileName("**").build())
+ .build(),
+ getReporter());
+ assert options.threadCount == ThreadUtils.NOT_SPECIFIED;
+ options.relocatorCompilation = true;
+ options.threadCount = getThreadCount();
+ options.programConsumer = consumer;
+ assert consumer != null;
+ options.dataResourceConsumer = consumer.getDataResourceConsumer();
+ // Set debug to ensure that we are writing all information to the application writer.
+ options.debug = true;
+ // We need to read stack maps since we are not processing anything.
+ options.testing.readInputStackMaps = true;
+ return options;
+ }
+
+ public Map<PackageReference, PackageReference> getMapping() {
+ return mapping;
+ }
+
+ @Keep
+ public static class Builder {
+
+ private final AndroidApp.Builder app;
+ private final Reporter reporter;
+ private final ImmutableMap.Builder<PackageReference, PackageReference> mapping =
+ ImmutableMap.builder();
+ private ClassFileConsumer consumer = null;
+ private int threadCount = ThreadUtils.NOT_SPECIFIED;
+ private boolean printVersion;
+ private boolean printHelp;
+
+ Builder() {
+ this(AndroidApp.builder());
+ }
+
+ Builder(DiagnosticsHandler handler) {
+ this(AndroidApp.builder(new Reporter(handler)));
+ }
+
+ Builder(AndroidApp.Builder builder) {
+ this.app = builder;
+ this.reporter = builder.getReporter();
+ }
+
+ /**
+ * Setting output to a path.
+ *
+ * <p>Setting the output path will override any previous set consumer or any previous set output
+ * path.
+ *
+ * @param outputPath Output path to write output to. A null argument will clear the program
+ * consumer / output.
+ */
+ public Builder setOutputPath(Path outputPath) {
+ if (outputPath == null) {
+ this.consumer = null;
+ return this;
+ }
+ this.consumer = new ArchiveConsumer(outputPath, true);
+ return this;
+ }
+
+ public Builder setPrintHelp(boolean printHelp) {
+ this.printHelp = printHelp;
+ return this;
+ }
+
+ public Builder setPrintVersion(boolean printVersion) {
+ this.printVersion = printVersion;
+ return this;
+ }
+
+ /** Signal an error. */
+ public void error(Diagnostic diagnostic) {
+ reporter.error(diagnostic);
+ }
+
+ /** Set the number of threads to use for the compilation */
+ public Builder setThreadCount(int threadCount) {
+ if (threadCount <= 0) {
+ reporter.error("Invalid threadCount: " + threadCount);
+ } else {
+ this.threadCount = threadCount;
+ }
+ return this;
+ }
+
+ /** Add program file resources. */
+ public Builder addProgramFiles(Path... files) {
+ return addProgramFiles(Arrays.asList(files));
+ }
+
+ /** Add program file resources. */
+ public Builder addProgramFiles(Collection<Path> files) {
+ guard(
+ () -> {
+ for (Path path : files) {
+ try {
+ app.addProgramFile(path);
+ } catch (CompilationError e) {
+ error(new PathOrigin(path), e);
+ }
+ }
+ });
+ return this;
+ }
+
+ /** Add program file resource. */
+ public Builder addProgramFile(Path file) {
+ guard(
+ () -> {
+ try {
+ app.addProgramFile(file);
+ } catch (CompilationError e) {
+ error(new PathOrigin(file), e);
+ }
+ });
+ return this;
+ }
+
+ public Builder addPackageMapping(PackageReference source, PackageReference destination) {
+ mapping.put(source, destination);
+ return this;
+ }
+
+ /**
+ * Set the program consumer.
+ *
+ * <p>Setting the program consumer will override any previous set consumer or any previous set
+ * output path.
+ *
+ * @param consumer ClassFile consumer to set as current. A null argument will clear the program
+ * consumer / output.
+ */
+ public Builder setConsumer(ClassFileConsumer consumer) {
+ // Setting an explicit program consumer resets any output-path/mode setup.
+ this.consumer = consumer;
+ return this;
+ }
+
+ private void validate() {
+ if (consumer == null) {
+ reporter.error(new StringDiagnostic("No output path or consumer has been specified"));
+ }
+ }
+
+ public RelocatorCommand build() throws CompilationFailedException {
+ try {
+ if (printHelp || printVersion) {
+ return new RelocatorCommand(printHelp, printVersion);
+ }
+ reporter.failIfPendingErrors();
+ validate();
+ reporter.failIfPendingErrors();
+ DexItemFactory factory = new DexItemFactory();
+ return new RelocatorCommand(
+ mapping.build(), app.build(), reporter, factory, consumer, threadCount);
+ } catch (AbortException e) {
+ throw new CompilationFailedException(e);
+ }
+ }
+
+ // Helper to signify an error.
+ void error(Origin origin, Throwable throwable) {
+ reporter.error(new ExceptionDiagnostic(throwable, origin));
+ }
+
+ // Helper to guard and handle exceptions.
+ void guard(Runnable action) {
+ try {
+ action.run();
+ } catch (CompilationError e) {
+ reporter.error(e.toStringDiagnostic());
+ } catch (AbortException e) {
+ // Error was reported and exception will be thrown by build.
+ }
+ }
+
+ /**
+ * Parse the Relocator command-line.
+ *
+ * <p>Parsing will set the supplied options or their default value if they have any.
+ *
+ * @param args Command-line arguments array.
+ * @param origin Origin description of the command-line arguments.
+ * @return Relocator command builder with state set up according to parsed command line.
+ */
+ public static Builder parse(String[] args, Origin origin) {
+ return parse(args, origin, RelocatorCommand.builder());
+ }
+
+ /**
+ * Parse the Relocator command-line.
+ *
+ * <p>Parsing will set the supplied options or their default value if they have any.
+ *
+ * @param args Command-line arguments array.
+ * @param origin Origin description of the command-line arguments.
+ * @param handler Custom defined diagnostics handler.
+ * @return Relocator command builder with state set up according to parsed command line.
+ */
+ public static Builder parse(String[] args, Origin origin, DiagnosticsHandler handler) {
+ return parse(args, origin, RelocatorCommand.builder(handler));
+ }
+
+ private static Builder parse(String[] args, Origin origin, Builder builder) {
+ String[] expandedArgs = FlagFile.expandFlagFiles(args, builder::error);
+ Path outputPath = null;
+ for (int i = 0; i < expandedArgs.length; i++) {
+ String arg = expandedArgs[i].trim();
+ String nextArg = null;
+ if (OPTIONS_WITH_PARAMETER.contains(arg)) {
+ if (++i < expandedArgs.length) {
+ nextArg = expandedArgs[i];
+ } else {
+ builder.error(
+ new StringDiagnostic("Missing parameter for " + expandedArgs[i - 1] + ".", origin));
+ break;
+ }
+ }
+ if (arg.length() == 0) {
+ continue;
+ }
+ switch (arg) {
+ case "--help":
+ builder.setPrintHelp(true);
+ break;
+ case "--version":
+ builder.setPrintVersion(true);
+ break;
+ case "--output":
+ assert nextArg != null;
+ if (outputPath != null) {
+ builder.error(
+ new StringDiagnostic(
+ "Cannot output both to '" + outputPath.toString() + "' and '" + nextArg + "'",
+ origin));
+ continue;
+ }
+ outputPath = Paths.get(nextArg);
+ break;
+ case "--input":
+ assert nextArg != null;
+ builder.addProgramFile(Paths.get(nextArg));
+ break;
+ case THREAD_COUNT_FLAG:
+ BaseCompilerCommandParser.parsePositiveIntArgument(
+ builder::error, arg, nextArg, origin, builder::setThreadCount);
+ break;
+ case "--map":
+ assert nextArg != null;
+ int separator = nextArg.indexOf("->");
+ if (separator < 0) {
+ builder.error(
+ new StringDiagnostic("--map " + nextArg + " is not on the form from->to"));
+ continue;
+ }
+ // TODO(b/155047633): Handle invalid package names.
+ builder.addPackageMapping(
+ Reference.packageFromString(nextArg.substring(0, separator)),
+ Reference.packageFromString(nextArg.substring(separator + 2)));
+ break;
+ default:
+ builder.error(new StringDiagnostic("Unknown argument: " + arg, origin));
+ }
+ }
+ if (outputPath == null) {
+ outputPath = Paths.get(".");
+ }
+ builder.setOutputPath(outputPath);
+ return builder;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/relocator/RelocatorCommandLine.java b/src/main/java/com/android/tools/r8/relocator/RelocatorCommandLine.java
new file mode 100644
index 0000000..f255aee
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/relocator/RelocatorCommandLine.java
@@ -0,0 +1,43 @@
+// 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.relocator;
+
+import static com.android.tools.r8.relocator.RelocatorCommand.USAGE_MESSAGE;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.Version;
+import com.android.tools.r8.origin.CommandLineOrigin;
+import com.android.tools.r8.utils.ExceptionUtils;
+
+public class RelocatorCommandLine {
+
+ /**
+ * EXPERIMENTAL - The CLI is subject to change. Command-line entry to Relocator.
+ *
+ * <p>See {@link RelocatorCommand#USAGE_MESSAGE} or run {@code relocator --help} for usage
+ * information.
+ */
+ public static void main(String[] args) {
+ if (args.length == 0) {
+ System.err.println(USAGE_MESSAGE);
+ System.exit(ExceptionUtils.STATUS_ERROR);
+ }
+ ExceptionUtils.withMainProgramHandler(() -> run(args));
+ }
+
+ static void run(String[] args) throws CompilationFailedException {
+ RelocatorCommand command =
+ RelocatorCommand.Builder.parse(args, CommandLineOrigin.INSTANCE).build();
+ if (command.isPrintHelp()) {
+ System.out.println(USAGE_MESSAGE);
+ return;
+ }
+ if (command.isPrintVersion()) {
+ System.out.println("Relocator " + Version.getVersionString());
+ return;
+ }
+ Relocator.run(command);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/relocator/RelocatorDiagnostic.java b/src/main/java/com/android/tools/r8/relocator/RelocatorDiagnostic.java
new file mode 100644
index 0000000..eeff5fa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/relocator/RelocatorDiagnostic.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.relocator;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+
+class RelocatorDiagnostic implements Diagnostic {
+
+ private final String message;
+
+ public RelocatorDiagnostic(String message) {
+ this.message = message;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+
+ @Override
+ public Position getPosition() {
+ return Position.UNKNOWN;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ return message;
+ }
+
+ static RelocatorDiagnostic typeRelocateAmbiguous(DexType type) {
+ return new RelocatorDiagnostic(
+ "Type '" + type.toSourceString() + "' can be relocated by multiple mappings.");
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java b/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java
new file mode 100644
index 0000000..c2f5837
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java
@@ -0,0 +1,172 @@
+// 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.relocator;
+
+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.DexItem;
+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.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.references.PackageReference;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableMap;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+class SimplePackagesRewritingMapper {
+
+ private final AppView<?> appView;
+ private final Map<DexType, DexString> typeMappings = new IdentityHashMap<>();
+
+ public SimplePackagesRewritingMapper(AppView<?> appView) {
+ this.appView = appView;
+ }
+
+ public NamingLens compute(Map<PackageReference, PackageReference> mapping) {
+ ImmutableMap.Builder<String, String> packingMappings = ImmutableMap.builder();
+ for (PackageReference key : mapping.keySet()) {
+ String source = key.getPackageName();
+ String target = mapping.get(key).getPackageName();
+ if (source.equals(target)) {
+ // No need for relocating identities.
+ continue;
+ }
+ if (source.isEmpty()) {
+ assert !target.isEmpty();
+ target = target + DescriptorUtils.JAVA_PACKAGE_SEPARATOR;
+ }
+ String sourceBinary = DescriptorUtils.getBinaryNameFromJavaType(source);
+ String targetBinary = DescriptorUtils.getBinaryNameFromJavaType(target);
+ packingMappings.put(sourceBinary, targetBinary);
+ DexString sourceDescriptor = appView.dexItemFactory().createString("L" + sourceBinary);
+ DexString targetDescriptor = appView.dexItemFactory().createString("L" + targetBinary);
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ DexString descriptor = clazz.type.descriptor;
+ // Check if descriptor can be a prefix.
+ if (descriptor.size <= sourceDescriptor.size) {
+ continue;
+ }
+ // Check if it is either the empty prefix or a fully qualified package.
+ if (sourceDescriptor.size != 1
+ && descriptor.content[sourceDescriptor.size]
+ != DescriptorUtils.DESCRIPTOR_PACKAGE_SEPARATOR) {
+ continue;
+ }
+ // Do a char-by-char comparison of the prefix.
+ if (!descriptor.startsWith(sourceDescriptor)) {
+ continue;
+ }
+ // This type should be mapped.
+ if (typeMappings.containsKey(clazz.type)) {
+ appView.options().reporter.error(RelocatorDiagnostic.typeRelocateAmbiguous(clazz.type));
+ appView.options().reporter.failIfPendingErrors();
+ }
+ DexString relocatedDescriptor =
+ clazz.type.descriptor.withNewPrefix(
+ sourceDescriptor, targetDescriptor, appView.dexItemFactory());
+ typeMappings.put(clazz.type, relocatedDescriptor);
+ }
+ }
+
+ return new RelocatorNamingLens(typeMappings, packingMappings.build(), appView.dexItemFactory());
+ }
+
+ Map<DexType, DexString> getTypeMappings() {
+ return typeMappings;
+ }
+
+ private static class RelocatorNamingLens extends NamingLens {
+
+ private final Map<DexType, DexString> typeMappings;
+ private final Map<String, String> packageMappings;
+ private final DexItemFactory factory;
+
+ private RelocatorNamingLens(
+ Map<DexType, DexString> typeMappings,
+ Map<String, String> packageMappings,
+ DexItemFactory factory) {
+ this.typeMappings = typeMappings;
+ this.packageMappings = packageMappings;
+ this.factory = factory;
+ }
+
+ @Override
+ public String lookupPackageName(String packageName) {
+ return packageMappings.getOrDefault(packageName, packageName);
+ }
+
+ @Override
+ public DexString lookupDescriptor(DexType type) {
+ if (type.isPrimitiveType() || type.isVoidType()) {
+ return type.descriptor;
+ }
+ if (type.isArrayType()) {
+ DexType baseType = type.toBaseType(factory);
+ if (baseType == null || baseType.isPrimitiveType()) {
+ return type.descriptor;
+ }
+ String baseDescriptor = typeMappings.getOrDefault(baseType, baseType.descriptor).toString();
+ return factory.createString(
+ DescriptorUtils.toArrayDescriptor(
+ type.getNumberOfLeadingSquareBrackets(), baseDescriptor));
+ }
+ return typeMappings.getOrDefault(type, type.descriptor);
+ }
+
+ @Override
+ public DexString lookupInnerName(InnerClassAttribute attribute, InternalOptions options) {
+ return attribute.getInnerName();
+ }
+
+ @Override
+ public DexString lookupName(DexMethod method) {
+ return method.name;
+ }
+
+ @Override
+ public DexString lookupMethodName(DexCallSite callSite) {
+ return callSite.methodName;
+ }
+
+ @Override
+ public DexString lookupName(DexField field) {
+ return field.name;
+ }
+
+ @Override
+ public boolean verifyNoOverlap(Map<DexType, DexString> map) {
+ return true;
+ }
+
+ @Override
+ public void forAllRenamedTypes(Consumer<DexType> consumer) {
+ typeMappings.keySet().forEach(consumer);
+ }
+
+ @Override
+ public <T extends DexItem> Map<String, T> getRenamedItems(
+ Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) {
+ return typeMappings.keySet().stream()
+ .filter(item -> (clazz.isInstance(item) && predicate.test(clazz.cast(item))))
+ .map(clazz::cast)
+ .collect(ImmutableMap.toImmutableMap(namer, i -> i));
+ }
+
+ @Override
+ public boolean verifyRenamingConsistentWithResolution(DexMethod item) {
+ return true;
+ }
+ }
+}
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 0de5218..3f657b9 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -8,7 +8,6 @@
import static com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult.isOverriding;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
@@ -71,7 +70,8 @@
import java.util.stream.Collectors;
/** Encapsulates liveness and reachability information for an application. */
-public class AppInfoWithLiveness extends AppInfoWithSubtyping implements InstantiatedSubTypeInfo {
+public class AppInfoWithLiveness extends AppInfoWithClassHierarchy
+ implements InstantiatedSubTypeInfo {
/** Set of reachable proto types that will be dead code eliminated. */
private final Set<DexType> deadProtoTypes;
/** Set of types that are mentioned in the program, but for which no definition exists. */
@@ -278,7 +278,7 @@
}
public AppInfoWithLiveness(
- AppInfoWithSubtyping appInfoWithSubtyping,
+ AppInfoWithClassHierarchy appInfoWithSubtyping,
Set<DexType> deadProtoTypes,
Set<DexType> missingTypes,
Set<DexType> liveTypes,
@@ -1129,7 +1129,7 @@
SingleResolutionResult resolution =
resolveMethod(initialResolutionHolder, method).asSingleResolution();
if (resolution == null
- || !resolution.isAccessibleForVirtualDispatchFrom(invocationClass, this)) {
+ || resolution.isAccessibleForVirtualDispatchFrom(invocationClass, this).isFalse()) {
return null;
}
// If the method is modeled, return the resolution.
@@ -1158,7 +1158,8 @@
// TODO(b/148769279): Disable lookup single target on lambda's for now.
if (resolvedHolder.isInterface()
&& resolvedHolder.isProgramClass()
- && isInstantiatedInterface(resolvedHolder.asProgramClass())) {
+ && objectAllocationInfoCollection.isImmediateInterfaceOfInstantiatedLambda(
+ resolvedHolder.asProgramClass())) {
singleTargetLookupCache.addToCache(refinedReceiverType, method, null);
return null;
}
@@ -1170,7 +1171,6 @@
refinedLowerBound = refinedLowerBoundClass.asProgramClass();
// TODO(b/154822960): Check if the lower bound is a subtype of the upper bound.
if (refinedLowerBound != null && !isSubtype(refinedLowerBound.type, refinedReceiverType)) {
- // We cannot trust the lower bound, so null it out.
refinedLowerBound = null;
}
}
@@ -1183,7 +1183,7 @@
if (lookupResult != null && !lookupResult.isIncomplete()) {
LookupTarget singleTarget = lookupResult.getSingleLookupTarget();
if (singleTarget != null && singleTarget.isMethodTarget()) {
- singleMethodTarget = singleTarget.asMethodTarget().getMethod();
+ singleMethodTarget = singleTarget.asMethodTarget().getDefinition();
}
}
if (receiverLowerBoundType == null) {
@@ -1205,11 +1205,11 @@
if (refinedReceiverClass.isProgramClass()) {
DexClassAndMethod clazzAndMethod =
resolution.lookupVirtualDispatchTarget(refinedReceiverClass.asProgramClass(), this);
- if (clazzAndMethod == null || isPinned(clazzAndMethod.getMethod().method)) {
+ if (clazzAndMethod == null || isPinned(clazzAndMethod.getDefinition().method)) {
// TODO(b/150640456): We should maybe only consider program methods.
return DexEncodedMethod.SENTINEL;
}
- return clazzAndMethod.getMethod();
+ return clazzAndMethod.getDefinition();
} else {
// TODO(b/150640456): We should maybe only consider program methods.
// If we resolved to a method on the refined receiver in the library, then we report the
diff --git a/src/main/java/com/android/tools/r8/shaking/ConsequentRootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/ConsequentRootSetBuilder.java
index 2e5edba..8288bde 100644
--- a/src/main/java/com/android/tools/r8/shaking/ConsequentRootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/ConsequentRootSetBuilder.java
@@ -3,15 +3,19 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.SubtypingInfo;
class ConsequentRootSetBuilder extends RootSetBuilder {
private final Enqueuer enqueuer;
- ConsequentRootSetBuilder(AppView<? extends AppInfoWithSubtyping> appView, Enqueuer enqueuer) {
- super(appView, appView.appInfo().app(), null);
+ ConsequentRootSetBuilder(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ SubtypingInfo subtypingInfo,
+ Enqueuer enqueuer) {
+ super(appView, subtypingInfo, null);
this.enqueuer = enqueuer;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
index c04ddf5..09aafdb 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -36,7 +36,7 @@
}
public DexEncodedMethod getContextMethod() {
- return context.getMethod();
+ return context.getDefinition();
}
@Override
@@ -71,22 +71,22 @@
@Override
public boolean registerInstanceFieldRead(DexField field) {
- return enqueuer.traceInstanceFieldRead(field, context.getMethod());
+ return enqueuer.traceInstanceFieldRead(field, context.getDefinition());
}
@Override
public boolean registerInstanceFieldReadFromMethodHandle(DexField field) {
- return enqueuer.traceInstanceFieldReadFromMethodHandle(field, context.getMethod());
+ return enqueuer.traceInstanceFieldReadFromMethodHandle(field, context.getDefinition());
}
@Override
public boolean registerInstanceFieldWrite(DexField field) {
- return enqueuer.traceInstanceFieldWrite(field, context.getMethod());
+ return enqueuer.traceInstanceFieldWrite(field, context.getDefinition());
}
@Override
public boolean registerInstanceFieldWriteFromMethodHandle(DexField field) {
- return enqueuer.traceInstanceFieldWriteFromMethodHandle(field, context.getMethod());
+ return enqueuer.traceInstanceFieldWriteFromMethodHandle(field, context.getDefinition());
}
@Override
@@ -96,43 +96,43 @@
@Override
public boolean registerStaticFieldRead(DexField field) {
- return enqueuer.traceStaticFieldRead(field, context.getMethod());
+ return enqueuer.traceStaticFieldRead(field, context.getDefinition());
}
@Override
public boolean registerStaticFieldReadFromMethodHandle(DexField field) {
- return enqueuer.traceStaticFieldReadFromMethodHandle(field, context.getMethod());
+ return enqueuer.traceStaticFieldReadFromMethodHandle(field, context.getDefinition());
}
@Override
public boolean registerStaticFieldWrite(DexField field) {
- return enqueuer.traceStaticFieldWrite(field, context.getMethod());
+ return enqueuer.traceStaticFieldWrite(field, context.getDefinition());
}
@Override
public boolean registerStaticFieldWriteFromMethodHandle(DexField field) {
- return enqueuer.traceStaticFieldWriteFromMethodHandle(field, context.getMethod());
+ return enqueuer.traceStaticFieldWriteFromMethodHandle(field, context.getDefinition());
}
@Override
public boolean registerConstClass(DexType type) {
- return enqueuer.traceConstClass(type, context.getMethod());
+ return enqueuer.traceConstClass(type, context.getDefinition());
}
@Override
public boolean registerCheckCast(DexType type) {
- return enqueuer.traceCheckCast(type, context.getMethod());
+ return enqueuer.traceCheckCast(type, context.getDefinition());
}
@Override
public boolean registerTypeReference(DexType type) {
- return enqueuer.traceTypeReference(type, context.getMethod());
+ return enqueuer.traceTypeReference(type, context.getDefinition());
}
@Override
public void registerMethodHandle(DexMethodHandle methodHandle, MethodHandleUse use) {
super.registerMethodHandle(methodHandle, use);
- enqueuer.traceMethodHandle(methodHandle, use, context.getMethod());
+ enqueuer.traceMethodHandle(methodHandle, use, context.getDefinition());
}
@Override
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 4fb2fe2..a1d6312 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -24,7 +24,6 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexAnnotation;
@@ -177,9 +176,9 @@
private Set<EnqueuerInvokeAnalysis> invokeAnalyses = Sets.newIdentityHashSet();
// Don't hold a direct pointer to app info (use appView).
- private AppInfoWithSubtyping appInfo;
- private final AppView<AppInfoWithSubtyping> appView;
- private final SubtypingInfo subtypingInfo;
+ private AppInfoWithClassHierarchy appInfo;
+ private final AppView<AppInfoWithClassHierarchy> appView;
+ private SubtypingInfo subtypingInfo;
private final InternalOptions options;
private RootSet rootSet;
private ProguardClassFilter dontWarnPatterns;
@@ -344,15 +343,15 @@
private final Set<DexProgramClass> classesWithSerializableLambdas = Sets.newIdentityHashSet();
Enqueuer(
- AppView<? extends AppInfoWithSubtyping> appView,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ SubtypingInfo subtypingInfo,
GraphConsumer keptGraphConsumer,
Mode mode) {
assert appView.appServices() != null;
InternalOptions options = appView.options();
this.appInfo = appView.appInfo();
- this.appView = appView.withSubtyping();
- this.subtypingInfo =
- new SubtypingInfo(appView.appInfo().app().asDirect().allClasses(), appView);
+ this.appView = appView.withClassHierarchy();
+ this.subtypingInfo = subtypingInfo;
this.forceProguardCompatibility = options.forceProguardCompatibility;
this.graphReporter = new GraphReporter(appView, keptGraphConsumer);
this.mode = mode;
@@ -762,20 +761,10 @@
DexProgramClass contextHolder = context.getHolder();
LambdaDescriptor descriptor = LambdaDescriptor.tryInfer(callSite, appInfo, contextHolder);
if (descriptor == null) {
- if (!isStringConcat(callSite.bootstrapMethod)) {
- if (options.reporter != null) {
- Diagnostic message =
- new StringDiagnostic(
- "Unknown bootstrap method " + callSite.bootstrapMethod, contextHolder.origin);
- options.reporter.warning(message);
- }
- }
-
- callSites.add(callSite);
return;
}
- DexEncodedMethod contextMethod = context.getMethod();
+ DexEncodedMethod contextMethod = context.getDefinition();
if (lambdaRewriter != null) {
assert contextMethod.getCode().isCfCode() : "Unexpected input type with lambdas";
CfCode code = contextMethod.getCode().asCfCode();
@@ -879,7 +868,7 @@
initClassReferences.put(
type, computeMinimumRequiredVisibilityForInitClassField(type, currentMethod.getHolder()));
- markTypeAsLive(type, classReferencedFromReporter(currentMethod.getMethod()));
+ markTypeAsLive(type, classReferencedFromReporter(currentMethod.getDefinition()));
markDirectAndIndirectClassInitializersAsLive(clazz);
return true;
}
@@ -956,7 +945,7 @@
boolean traceInvokeDirect(DexMethod invokedMethod, ProgramMethod context) {
DexProgramClass currentHolder = context.getHolder();
- DexEncodedMethod currentMethod = context.getMethod();
+ DexEncodedMethod currentMethod = context.getDefinition();
boolean skipTracing =
registerDeferredActionForDeadProtoBuilder(
invokedMethod.holder,
@@ -989,12 +978,12 @@
boolean traceInvokeDirectFromLambda(DexMethod invokedMethod, ProgramMethod context) {
return traceInvokeDirect(
- invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context.getMethod()));
+ invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context.getDefinition()));
}
private boolean traceInvokeDirect(
DexMethod invokedMethod, ProgramMethod context, KeepReason reason) {
- DexEncodedMethod currentMethod = context.getMethod();
+ DexEncodedMethod currentMethod = context.getDefinition();
if (!registerMethodWithTargetAndContext(directInvokes, invokedMethod, currentMethod)) {
return false;
}
@@ -1012,12 +1001,12 @@
boolean traceInvokeInterfaceFromLambda(DexMethod invokedMethod, ProgramMethod context) {
return traceInvokeInterface(
- invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context.getMethod()));
+ invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context.getDefinition()));
}
private boolean traceInvokeInterface(
DexMethod method, ProgramMethod context, KeepReason keepReason) {
- DexEncodedMethod currentMethod = context.getMethod();
+ DexEncodedMethod currentMethod = context.getDefinition();
if (!registerMethodWithTargetAndContext(interfaceInvokes, method, currentMethod)) {
return false;
}
@@ -1035,12 +1024,12 @@
boolean traceInvokeStaticFromLambda(DexMethod invokedMethod, ProgramMethod context) {
return traceInvokeStatic(
- invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context.getMethod()));
+ invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context.getDefinition()));
}
private boolean traceInvokeStatic(
DexMethod invokedMethod, ProgramMethod context, KeepReason reason) {
- DexEncodedMethod currentMethod = context.getMethod();
+ DexEncodedMethod currentMethod = context.getDefinition();
DexItemFactory dexItemFactory = appView.dexItemFactory();
if (dexItemFactory.classMethods.isReflectiveClassLookup(invokedMethod)
|| dexItemFactory.atomicFieldUpdaterMethods.isFieldUpdater(invokedMethod)) {
@@ -1072,7 +1061,7 @@
}
boolean traceInvokeSuper(DexMethod invokedMethod, ProgramMethod context) {
- DexEncodedMethod currentMethod = context.getMethod();
+ DexEncodedMethod currentMethod = context.getDefinition();
// We have to revisit super invokes based on the context they are found in. The same
// method descriptor will hit different targets, depending on the context it is used in.
DexMethod actualTarget = getInvokeSuperTarget(invokedMethod, currentMethod);
@@ -1093,21 +1082,22 @@
boolean traceInvokeVirtualFromLambda(DexMethod invokedMethod, ProgramMethod context) {
return traceInvokeVirtual(
- invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context.getMethod()));
+ invokedMethod, context, KeepReason.invokedFromLambdaCreatedIn(context.getDefinition()));
}
private boolean traceInvokeVirtual(
DexMethod invokedMethod, ProgramMethod context, KeepReason reason) {
if (invokedMethod == appView.dexItemFactory().classMethods.newInstance
|| invokedMethod == appView.dexItemFactory().constructorMethods.newInstance) {
- pendingReflectiveUses.add(context.getMethod());
+ pendingReflectiveUses.add(context.getDefinition());
} else if (appView.dexItemFactory().classMethods.isReflectiveMemberLookup(invokedMethod)) {
// Implicitly add -identifiernamestring rule for the Java reflection in use.
identifierNameStrings.add(invokedMethod);
// Revisit the current method to implicitly add -keep rule for items with reflective access.
- pendingReflectiveUses.add(context.getMethod());
+ pendingReflectiveUses.add(context.getDefinition());
}
- if (!registerMethodWithTargetAndContext(virtualInvokes, invokedMethod, context.getMethod())) {
+ if (!registerMethodWithTargetAndContext(
+ virtualInvokes, invokedMethod, context.getDefinition())) {
return false;
}
if (Log.ENABLED) {
@@ -1119,7 +1109,7 @@
}
boolean traceNewInstance(DexType type, ProgramMethod context) {
- DexEncodedMethod currentMethod = context.getMethod();
+ DexEncodedMethod currentMethod = context.getDefinition();
boolean skipTracing =
registerDeferredActionForDeadProtoBuilder(
type, currentMethod, () -> workList.enqueueTraceNewInstanceAction(type, context));
@@ -1140,7 +1130,7 @@
type,
context,
InstantiationReason.LAMBDA,
- KeepReason.invokedFromLambdaCreatedIn(context.getMethod()));
+ KeepReason.invokedFromLambdaCreatedIn(context.getDefinition()));
}
private boolean traceNewInstance(
@@ -1148,7 +1138,7 @@
ProgramMethod context,
InstantiationReason instantiationReason,
KeepReason keepReason) {
- DexEncodedMethod currentMethod = context.getMethod();
+ DexEncodedMethod currentMethod = context.getDefinition();
DexProgramClass clazz = getProgramClassOrNull(type);
if (clazz != null) {
if (clazz.isAnnotation() || clazz.isInterface()) {
@@ -1938,12 +1928,6 @@
for (DexType iface : descriptor.interfaces) {
checkLambdaInterface(iface, context);
objectAllocationInfoCollection.recordInstantiatedLambdaInterface(iface, descriptor, appInfo);
- // TODO(b/150277553): Lambdas should be accurately traced and thus not be added here.
- assert lambdaRewriter == null;
- DexProgramClass clazz = getProgramClassOrNull(iface);
- if (clazz != null) {
- objectAllocationInfoCollection.recordInstantiatedInterface(clazz, appInfo);
- }
}
}
@@ -2054,26 +2038,26 @@
// The validity of the reachable method is checked at the point it becomes "reachable" and is
// resolved. If the method is private, then the dispatch is not "virtual" and the method is
// simply marked live on its holder.
- if (resolutionMethod.getMethod().isPrivateMethod()) {
+ if (resolutionMethod.getDefinition().isPrivateMethod()) {
markVirtualMethodAsLive(
resolutionMethod.getHolder(),
- resolutionMethod.getMethod(),
+ resolutionMethod.getDefinition(),
graphReporter.reportReachableMethodAsLive(
- resolutionMethod.getMethod().method, resolutionMethod));
+ resolutionMethod.getDefinition().method, resolutionMethod));
return;
}
// Otherwise, we set the initial holder type to be the holder of the reachable method, which
// ensures that access will be generally valid.
SingleResolutionResult resolution =
new SingleResolutionResult(
- initialHolder, resolutionMethod.getHolder(), resolutionMethod.getMethod());
+ initialHolder, resolutionMethod.getHolder(), resolutionMethod.getDefinition());
LookupTarget lookup = resolution.lookupVirtualDispatchTarget(instantiation, appInfo);
if (lookup != null) {
markVirtualDispatchTargetAsLive(
lookup,
programMethod ->
graphReporter.reportReachableMethodAsLive(
- resolutionMethod.getMethod().method, programMethod));
+ resolutionMethod.getDefinition().method, programMethod));
}
}
@@ -2131,7 +2115,7 @@
if (instantiation.isClass()) {
// TODO(b/149976493): We need to mark these for lambdas too!
markOverridesAsLibraryMethodOverrides(
- instantiation.asClass(), lookup.asMethodTarget().getMethod().method);
+ instantiation.asClass(), lookup.asMethodTarget().getDefinition().method);
}
}
@@ -2453,7 +2437,8 @@
markMethodAsTargeted(resolvedHolder, resolvedMethod, reason);
DexProgramClass context = contextOrNull == null ? null : contextOrNull.getHolder();
- if (contextOrNull != null && !resolution.isAccessibleForVirtualDispatchFrom(context, appInfo)) {
+ if (contextOrNull != null
+ && resolution.isAccessibleForVirtualDispatchFrom(context, appInfo).isFalse()) {
// Not accessible from this context, so this call will cause a runtime exception.
return;
}
@@ -2498,9 +2483,9 @@
private void markVirtualDispatchTargetAsLive(
DexClassAndMethod target, Function<ProgramMethod, KeepReasonWitness> reason) {
ProgramMethod programMethod = target.asProgramMethod();
- if (programMethod != null && !programMethod.getMethod().isAbstract()) {
+ if (programMethod != null && !programMethod.getDefinition().isAbstract()) {
markVirtualMethodAsLive(
- programMethod.getHolder(), programMethod.getMethod(), reason.apply(programMethod));
+ programMethod.getHolder(), programMethod.getDefinition(), reason.apply(programMethod));
}
}
@@ -2510,7 +2495,7 @@
if (implementationMethod != null) {
enqueueMarkMethodLiveAction(
implementationMethod.getHolder(),
- implementationMethod.getMethod(),
+ implementationMethod.getDefinition(),
reason.apply(implementationMethod));
}
}
@@ -2678,14 +2663,14 @@
}
void addLiveMethod(ProgramMethod method) {
- DexMethod signature = method.getMethod().method;
+ DexMethod signature = method.getDefinition().method;
assert !liveMethods.containsKey(signature);
liveMethods.put(signature, method);
}
void addLiveAndPinnedMethod(ProgramMethod method) {
addLiveMethod(method);
- pinnedMethods.add(method.getMethod().method);
+ pinnedMethods.add(method.getDefinition().method);
}
void amendApplication(Builder appBuilder) {
@@ -2714,9 +2699,9 @@
fakeReason);
}
for (ProgramMethod liveMethod : liveMethods.values()) {
- assert !enqueuer.targetedMethods.contains(liveMethod.getMethod());
+ assert !enqueuer.targetedMethods.contains(liveMethod.getDefinition());
DexProgramClass holder = liveMethod.getHolder();
- DexEncodedMethod method = liveMethod.getMethod();
+ DexEncodedMethod method = liveMethod.getDefinition();
enqueuer.markMethodAsTargeted(holder, method, fakeReason);
enqueuer.enqueueMarkMethodLiveAction(holder, method, fakeReason);
}
@@ -2741,8 +2726,10 @@
// Now all additions are computed, the application is atomically extended with those additions.
Builder appBuilder = appInfo.app().asDirect().builder();
additions.amendApplication(appBuilder);
- appInfo = new AppInfoWithSubtyping(appBuilder.build());
+ DirectMappedDexApplication app = appBuilder.build();
+ appInfo = new AppInfoWithClassHierarchy(app);
appView.setAppInfo(appInfo);
+ subtypingInfo = new SubtypingInfo(app.allClasses(), app);
// Finally once all synthesized items "exist" it is now safe to continue tracing. The new work
// items are enqueued and the fixed point will continue once this subroutine returns.
@@ -2752,7 +2739,7 @@
private void synthesizeInterfaceMethodBridges(SyntheticAdditions additions) {
for (ProgramMethod bridge : syntheticInterfaceMethodBridges.values()) {
DexProgramClass holder = bridge.getHolder();
- DexEncodedMethod method = bridge.getMethod();
+ DexEncodedMethod method = bridge.getDefinition();
holder.addVirtualMethod(method);
additions.addLiveAndPinnedMethod(bridge);
}
@@ -2811,7 +2798,7 @@
return true;
}
- private AppInfoWithLiveness createAppInfo(AppInfoWithSubtyping appInfo) {
+ private AppInfoWithLiveness createAppInfo(AppInfoWithClassHierarchy appInfo) {
// Once all tracing is done, we generate accessor methods for lambdas.
// These are assumed to be simple forwarding or access flag updates, thus no further tracing
// is needed. These cannot be generated as part of lambda synthesis as changing a direct method
@@ -3127,7 +3114,7 @@
}
}
ConsequentRootSetBuilder consequentSetBuilder =
- new ConsequentRootSetBuilder(appView, this);
+ new ConsequentRootSetBuilder(appView, subtypingInfo, this);
IfRuleEvaluator ifRuleEvaluator =
new IfRuleEvaluator(
appView,
@@ -3239,7 +3226,7 @@
}
private ConsequentRootSet computeDelayedInterfaceMethodSyntheticBridges() {
- RootSetBuilder builder = new RootSetBuilder(appView);
+ RootSetBuilder builder = new RootSetBuilder(appView, subtypingInfo);
for (DelayedRootSetActionItem delayedRootSetActionItem : rootSet.delayedRootSetActionItems) {
if (delayedRootSetActionItem.isInterfaceMethodSyntheticBridgeAction()) {
handleInterfaceMethodSyntheticBridgeAction(
@@ -3255,17 +3242,17 @@
InterfaceMethodSyntheticBridgeAction action, RootSetBuilder builder) {
ProgramMethod methodToKeep = action.getMethodToKeep();
ProgramMethod singleTarget = action.getSingleTarget();
- DexEncodedMethod singleTargetMethod = singleTarget.getMethod();
+ DexEncodedMethod singleTargetMethod = singleTarget.getDefinition();
if (rootSet.noShrinking.containsKey(singleTargetMethod.method)) {
return;
}
if (methodToKeep != singleTarget) {
- assert null == methodToKeep.getHolder().lookupMethod(methodToKeep.getMethod().method);
+ assert null == methodToKeep.getHolder().lookupMethod(methodToKeep.getDefinition().method);
ProgramMethod old =
- syntheticInterfaceMethodBridges.put(methodToKeep.getMethod().method, methodToKeep);
+ syntheticInterfaceMethodBridges.put(methodToKeep.getDefinition().method, methodToKeep);
if (old == null) {
if (singleTargetMethod.isLibraryMethodOverride().isTrue()) {
- methodToKeep.getMethod().setLibraryMethodOverride(OptionalBool.TRUE);
+ methodToKeep.getDefinition().setLibraryMethodOverride(OptionalBool.TRUE);
}
DexProgramClass singleTargetHolder = singleTarget.getHolder();
assert singleTargetHolder.isInterface();
@@ -3351,7 +3338,7 @@
return false;
}
DexProgramClass clazz = programMethod.getHolder();
- DexEncodedMethod method = programMethod.getMethod();
+ DexEncodedMethod method = programMethod.getDefinition();
assert method.isVirtualMethod();
if (method.isAbstract() || method.isPrivateMethod()) {
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java
index a949527..743f0df 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java
@@ -5,41 +5,49 @@
package com.android.tools.r8.shaking;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.shaking.Enqueuer.Mode;
import java.util.Set;
public class EnqueuerFactory {
public static Enqueuer createForInitialTreeShaking(
- AppView<? extends AppInfoWithSubtyping> appView) {
- return new Enqueuer(appView, null, Mode.INITIAL_TREE_SHAKING);
+ AppView<? extends AppInfoWithClassHierarchy> appView, SubtypingInfo subtypingInfo) {
+ return new Enqueuer(appView, subtypingInfo, null, Mode.INITIAL_TREE_SHAKING);
}
public static Enqueuer createForFinalTreeShaking(
- AppView<? extends AppInfoWithSubtyping> appView,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ SubtypingInfo subtypingInfo,
GraphConsumer keptGraphConsumer,
Set<DexType> initialMissingTypes) {
- Enqueuer enqueuer = new Enqueuer(appView, keptGraphConsumer, Mode.FINAL_TREE_SHAKING);
+ Enqueuer enqueuer =
+ new Enqueuer(appView, subtypingInfo, keptGraphConsumer, Mode.FINAL_TREE_SHAKING);
appView.withProtoShrinker(
shrinker -> enqueuer.setInitialDeadProtoTypes(shrinker.getDeadProtoTypes()));
enqueuer.setInitialMissingTypes(initialMissingTypes);
return enqueuer;
}
- public static Enqueuer createForMainDexTracing(AppView<? extends AppInfoWithSubtyping> appView) {
- return createForMainDexTracing(appView, null);
+ public static Enqueuer createForMainDexTracing(
+ AppView<? extends AppInfoWithClassHierarchy> appView, SubtypingInfo subtypingInfo) {
+ return createForMainDexTracing(appView, subtypingInfo, null);
}
public static Enqueuer createForMainDexTracing(
- AppView<? extends AppInfoWithSubtyping> appView, GraphConsumer keptGraphConsumer) {
- return new Enqueuer(appView, keptGraphConsumer, Mode.MAIN_DEX_TRACING);
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ SubtypingInfo subtypingInfo,
+ GraphConsumer keptGraphConsumer) {
+ return new Enqueuer(appView, subtypingInfo, keptGraphConsumer, Mode.MAIN_DEX_TRACING);
}
public static Enqueuer createForWhyAreYouKeeping(
- AppView<? extends AppInfoWithSubtyping> appView, GraphConsumer keptGraphConsumer) {
- return new Enqueuer(appView, keptGraphConsumer, Mode.WHY_ARE_YOU_KEEPING);
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ SubtypingInfo subtypingInfo,
+ GraphConsumer keptGraphConsumer) {
+ return new Enqueuer(appView, subtypingInfo, keptGraphConsumer, Mode.WHY_ARE_YOU_KEEPING);
}
}
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 6b222d7..4ff3594 100644
--- a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
+++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -249,10 +249,10 @@
public KeepReasonWitness reportReachableMethodAsLive(
DexMethod overriddenMethod, ProgramMethod derivedMethod) {
- if (keptGraphConsumer != null && overriddenMethod != derivedMethod.getMethod().method) {
+ if (keptGraphConsumer != null && overriddenMethod != derivedMethod.getDefinition().method) {
return reportEdge(
getMethodGraphNode(overriddenMethod),
- getMethodGraphNode(derivedMethod.getMethod().method),
+ getMethodGraphNode(derivedMethod.getDefinition().method),
EdgeKind.OverridingMethod);
}
return KeepReasonWitness.INSTANCE;
@@ -266,7 +266,7 @@
if (keptGraphConsumer != null && instantiation.isClass()) {
return reportEdge(
getClassGraphNode(instantiation.asClass().type),
- getMethodGraphNode(derivedMethod.getMethod().method),
+ getMethodGraphNode(derivedMethod.getDefinition().method),
EdgeKind.IsLibraryMethod);
}
return KeepReasonWitness.INSTANCE;
diff --git a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
index 225aff0..d8fbb6d 100644
--- a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
+++ b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
@@ -5,7 +5,7 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexDefinition;
@@ -36,7 +36,7 @@
public class IfRuleEvaluator {
- private final AppView<? extends AppInfoWithSubtyping> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final SubtypingInfo subtypingInfo;
private final Enqueuer enqueuer;
private final ExecutorService executorService;
@@ -45,7 +45,7 @@
private final ConsequentRootSetBuilder rootSetBuilder;
IfRuleEvaluator(
- AppView<? extends AppInfoWithSubtyping> appView,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
SubtypingInfo subtypingInfo,
Enqueuer enqueuer,
ExecutorService executorService,
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepReason.java b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
index dd76e19..62a67ee 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepReason.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
@@ -26,7 +26,7 @@
}
static KeepReason instantiatedIn(DexEncodedMethod method) {
- return new InstatiatedIn(method);
+ return new InstantiatedIn(method);
}
public static KeepReason invokedViaSuperFrom(DexEncodedMethod from) {
@@ -42,7 +42,7 @@
}
public static KeepReason invokedFrom(ProgramMethod context) {
- return invokedFrom(context.getHolder(), context.getMethod());
+ return invokedFrom(context.getHolder(), context.getDefinition());
}
public static KeepReason invokedFromLambdaCreatedIn(DexEncodedMethod method) {
@@ -97,9 +97,9 @@
}
}
- public static class InstatiatedIn extends BasedOnOtherMethod {
+ public static class InstantiatedIn extends BasedOnOtherMethod {
- private InstatiatedIn(DexEncodedMethod method) {
+ private InstantiatedIn(DexEncodedMethod method) {
super(method);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java b/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
index 15d4e4b..e686f98 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.shaking;
import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
@@ -27,10 +27,11 @@
new AnnotationDirectReferenceCollector();
private final DirectReferencesCollector codeDirectReferenceCollector;
- private final AppInfoWithSubtyping appInfo;
+ private final AppInfoWithClassHierarchy appInfo;
private final Consumer<DexType> consumer;
- public MainDexDirectReferenceTracer(AppInfoWithSubtyping appInfo, Consumer<DexType> consumer) {
+ public MainDexDirectReferenceTracer(
+ AppInfoWithClassHierarchy appInfo, Consumer<DexType> consumer) {
this.codeDirectReferenceCollector = new DirectReferencesCollector(appInfo.dexItemFactory());
this.appInfo = appInfo;
this.consumer = consumer;
@@ -61,7 +62,7 @@
}
public static boolean hasReferencesOutsideFromCode(
- AppInfoWithSubtyping appInfo, DexEncodedMethod method, Set<DexType> classes) {
+ AppInfoWithClassHierarchy appInfo, DexEncodedMethod method, Set<DexType> classes) {
BooleanBox result = new BooleanBox();
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java b/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
index b46840b..fc34925 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
@@ -5,7 +5,7 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -28,7 +28,7 @@
public class MainDexListBuilder {
private final Set<DexType> roots;
- private final AppInfoWithSubtyping appInfo;
+ private final AppInfoWithClassHierarchy appInfo;
private final Map<DexType, Boolean> annotationTypeContainEnum;
private final DirectMappedDexApplication dexApplication;
private final MainDexClasses.Builder mainDexClassesBuilder;
@@ -52,7 +52,7 @@
*/
public MainDexListBuilder(Set<DexProgramClass> roots, DirectMappedDexApplication application) {
this.dexApplication = application;
- this.appInfo = new AppInfoWithSubtyping(dexApplication);
+ this.appInfo = new AppInfoWithClassHierarchy(dexApplication);
// Only consider program classes for the root set.
this.roots = SetUtils.mapIdentityHashSet(roots, DexProgramClass::getType);
mainDexClassesBuilder = MainDexClasses.builder(appInfo).addRoots(this.roots);
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 3598fd8..7d8d88a 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -251,8 +251,9 @@
adaptClassStrings.addPattern(pattern);
}
- public void addAdaptResourceFilenames(ProguardPathList pattern) {
+ public Builder addAdaptResourceFilenames(ProguardPathList pattern) {
adaptResourceFilenames.addPattern(pattern);
+ return this;
}
public void addAdaptResourceFileContents(ProguardPathList pattern) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
index edb901b..52c3b4c 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexProgramClass;
@@ -83,7 +83,7 @@
}
Iterable<DexProgramClass> relevantCandidatesForRule(
- AppView<? extends AppInfoWithSubtyping> appView,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
SubtypingInfo subtypingInfo,
Iterable<DexProgramClass> defaultValue) {
if (hasInheritanceClassName() && getInheritanceClassName().hasSpecificType()) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardPathList.java b/src/main/java/com/android/tools/r8/shaking/ProguardPathList.java
index edcc4f6..742f2ef 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardPathList.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardPathList.java
@@ -35,7 +35,7 @@
return this;
}
- ProguardPathList build() {
+ public ProguardPathList build() {
List<FileNameMatcher> matchers = this.matchers.build();
if (matchers. size() > 0) {
return new PathList(matchers);
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 2065def..846710b 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -8,7 +8,7 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.BottomUpClassHierarchyTraversal;
import com.android.tools.r8.graph.DexAnnotation;
@@ -77,7 +77,7 @@
public class RootSetBuilder {
- private final AppView<? extends AppInfoWithSubtyping> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final SubtypingInfo subtypingInfo;
private final DirectMappedDexApplication application;
private final Iterable<? extends ProguardConfigurationRule> rules;
@@ -114,18 +114,19 @@
private final Set<ProguardIfRule> ifRules = Sets.newIdentityHashSet();
public RootSetBuilder(
- AppView<? extends AppInfoWithSubtyping> appView,
- DexApplication application,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ SubtypingInfo subtypingInfo,
Iterable<? extends ProguardConfigurationRule> rules) {
this.appView = appView;
- this.application = application.asDirect();
- this.subtypingInfo = new SubtypingInfo(this.application.allClasses(), this.application);
+ this.subtypingInfo = subtypingInfo;
+ this.application = appView.appInfo().app().asDirect();
this.rules = rules;
this.options = appView.options();
}
- public RootSetBuilder(AppView<? extends AppInfoWithSubtyping> appView) {
- this(appView, appView.appInfo().app(), null);
+ public RootSetBuilder(
+ AppView<? extends AppInfoWithClassHierarchy> appView, SubtypingInfo subtypingInfo) {
+ this(appView, subtypingInfo, null);
}
void handleMatchedAnnotation(AnnotationMatchResult annotation) {
@@ -571,10 +572,10 @@
resolutionResult.getResolvedHolder().asProgramClass(),
resolutionResult.getResolvedMethod());
ProgramMethod methodToKeep =
- canInsertForwardingMethod(originalClazz, resolutionMethod.getMethod())
+ canInsertForwardingMethod(originalClazz, resolutionMethod.getDefinition())
? new ProgramMethod(
originalClazz,
- resolutionMethod.getMethod().toForwardingMethod(originalClazz, appView))
+ resolutionMethod.getDefinition().toForwardingMethod(originalClazz, appView))
: resolutionMethod;
delayedRootSetActionItems.add(
@@ -591,9 +592,9 @@
rule);
}
DexDefinition precondition =
- testAndGetPrecondition(methodToKeep.getMethod(), preconditionSupplier);
+ testAndGetPrecondition(methodToKeep.getDefinition(), preconditionSupplier);
rootSetBuilder.addItemToSets(
- methodToKeep.getMethod(), context, rule, precondition, ifRule);
+ methodToKeep.getDefinition(), context, rule, precondition, ifRule);
}));
}
}
@@ -604,7 +605,7 @@
}
private void markMatchingOverriddenMethods(
- AppInfoWithSubtyping appInfoWithSubtyping,
+ AppInfoWithClassHierarchy appInfoWithSubtyping,
DexClass clazz,
Collection<ProguardMemberRule> memberKeepRules,
ProguardConfigurationRule rule,
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 95fa85d..bb90d42 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -344,6 +344,7 @@
ObjectAllocationInfoCollection allocationInfo = appInfo.getObjectAllocationInfoCollection();
if (allocationInfo.isInstantiatedDirectly(sourceClass)
|| allocationInfo.isInterfaceWithUnknownSubtypeHierarchy(sourceClass)
+ || allocationInfo.isImmediateInterfaceOfInstantiatedLambda(sourceClass)
|| appInfo.isPinned(sourceClass.type)
|| pinnedTypes.contains(sourceClass.type)
|| appInfo.neverMerge.contains(sourceClass.type)) {
@@ -756,7 +757,7 @@
Box<Boolean> found = new Box<>(false);
lookupResult.forEach(
interfaceTarget -> {
- if (interfaceTarget.getMethod() == method) {
+ if (interfaceTarget.getDefinition() == method) {
return;
}
DexClass enclosingClass = interfaceTarget.getHolder();
diff --git a/src/main/java/com/android/tools/r8/utils/ChainableConsumer.java b/src/main/java/com/android/tools/r8/utils/ChainableConsumer.java
new file mode 100644
index 0000000..7d69754
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ChainableConsumer.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+public interface ChainableConsumer<T> {
+
+ ChainableConsumer<T> accept(T value);
+}
diff --git a/src/main/java/com/android/tools/r8/utils/ChainableStringConsumer.java b/src/main/java/com/android/tools/r8/utils/ChainableStringConsumer.java
new file mode 100644
index 0000000..d1636c5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ChainableStringConsumer.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import java.util.function.Consumer;
+
+public interface ChainableStringConsumer extends ChainableConsumer<String> {
+
+ @Override
+ ChainableStringConsumer accept(String string);
+
+ static ChainableStringConsumer wrap(Consumer<String> consumer) {
+ return new ChainableStringConsumer() {
+ @Override
+ public ChainableStringConsumer accept(String value) {
+ consumer.accept(value);
+ return this;
+ }
+ };
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java b/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
new file mode 100644
index 0000000..0979b27
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+public class ConsumerUtils {
+
+ public static <T> ThrowingConsumer<T, RuntimeException> emptyThrowingConsumer() {
+ return ignore -> {};
+ }
+}
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 3e82b0d..7e34093 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -187,7 +187,6 @@
enableVerticalClassMerging = false;
enableEnumUnboxing = false;
enableUninstantiatedTypeOptimization = false;
- enableUnusedArgumentRemoval = false;
outline.enabled = false;
enableEnumValueOptimization = false;
enableValuePropagation = false;
@@ -222,14 +221,13 @@
public boolean enableHorizontalClassMerging = true;
public boolean enableVerticalClassMerging = true;
public boolean enableArgumentRemoval = true;
- public boolean enableUnusedArgumentRemoval = true;
public boolean enableUnusedInterfaceRemoval = true;
public boolean enableDevirtualization = true;
public boolean enableNonNullTracking = true;
public boolean enableInlining =
!Version.isDevelopmentVersion()
|| System.getProperty("com.android.tools.r8.disableinlining") == null;
- public boolean enableEnumUnboxing = false;
+ public boolean enableEnumUnboxing = true;
// TODO(b/141451716): Evaluate the effect of allowing inlining in the inlinee.
public boolean applyInliningToInlinee =
System.getProperty("com.android.tools.r8.applyInliningToInlinee") != null;
@@ -336,6 +334,9 @@
if (!isGeneratingClassFiles()) {
marker.setMinApi(minApiLevel);
}
+ if (desugaredLibraryConfiguration.getIdentifier() != null) {
+ marker.setDesugaredLibraryIdentifiers(desugaredLibraryConfiguration.getIdentifier());
+ }
if (Version.isDevelopmentVersion()) {
marker.setSha1(VersionProperties.INSTANCE.getSha());
}
@@ -365,12 +366,17 @@
return desugaredLibraryConfiguration.isLibraryCompilation();
}
+ public boolean isRelocatorCompilation() {
+ return relocatorCompilation;
+ }
+
public boolean shouldBackportMethods() {
return !hasConsumer() || isGeneratingDex();
}
public boolean shouldKeepStackMapTable() {
return isDesugaredLibraryCompilation()
+ || isRelocatorCompilation()
|| getProguardConfiguration().getKeepAttributes().stackMapTable;
}
@@ -675,6 +681,8 @@
public DesugaredLibraryConfiguration desugaredLibraryConfiguration =
DesugaredLibraryConfiguration.empty();
+ public boolean relocatorCompilation = false;
+
// If null, no keep rules are recorded.
// If non null it records desugared library APIs used by the program.
public StringConsumer desugaredLibraryKeepRuleConsumer = null;
@@ -1099,6 +1107,7 @@
public boolean addCallEdgesForLibraryInvokes = false;
public boolean allowCheckDiscardedErrors = false;
+ public boolean allowInjectedAnnotationMethods = false;
public boolean allowTypeErrors =
!Version.isDevelopmentVersion()
|| System.getProperty("com.android.tools.r8.allowTypeErrors") != null;
@@ -1184,12 +1193,6 @@
enableNameReflectionOptimization = false;
}
- @VisibleForTesting
- public void enableEnumUnboxing() {
- assert !enableEnumUnboxing;
- enableEnumUnboxing = true;
- }
-
// TODO(b/69963623): Remove this once enabled.
@VisibleForTesting
public void enablePropagationOfConstantsAtCallSites() {
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 9c18d3a..8cdce08 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.ResourceException;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfPosition;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+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.Code;
@@ -261,7 +261,7 @@
}
public static ClassNameMapper run(
- AppView<AppInfoWithSubtyping> appView,
+ AppView<AppInfoWithClassHierarchy> appView,
DexApplication application,
AndroidApp inputApp,
NamingLens namingLens) {
@@ -436,7 +436,7 @@
}
private static boolean verifyMethodsAreKeptDirectlyOrIndirectly(
- AppView<AppInfoWithSubtyping> appView, List<DexEncodedMethod> methods) {
+ AppView<AppInfoWithClassHierarchy> appView, List<DexEncodedMethod> methods) {
if (appView.options().isGeneratingClassFiles()) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/utils/StringUtils.java b/src/main/java/com/android/tools/r8/utils/StringUtils.java
index 02e83de..902924a 100644
--- a/src/main/java/com/android/tools/r8/utils/StringUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.utils;
import com.android.tools.r8.errors.Unreachable;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
@@ -333,4 +335,10 @@
public static String replaceAll(String subject, String target, String replacement) {
return subject.replaceAll(Pattern.quote(target), Matcher.quoteReplacement(replacement));
}
+
+ public static String stacktraceAsString(Throwable throwable) {
+ StringWriter sw = new StringWriter();
+ throwable.printStackTrace(new PrintWriter(sw));
+ return sw.toString();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
index 11e6692..08ce5be 100644
--- a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
@@ -90,9 +90,7 @@
String classPath =
addR8ExternalDeps
- ? r8jar.toAbsolutePath().toString()
- + CLASSPATH_SEPARATOR
- + ToolHelper.DEPS_NOT_RELOCATED
+ ? r8jar.toAbsolutePath().toString() + CLASSPATH_SEPARATOR + ToolHelper.DEPS
: r8jar.toAbsolutePath().toString();
List<String> command = new ArrayList<>();
diff --git a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
index 30f6489..899093a 100644
--- a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
+++ b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
@@ -1824,6 +1824,9 @@
.put(
"util.concurrent.Executors.newCachedThreadPoolLjava_util_concurrent_ThreadFactory.Executors_newCachedThreadPool_A01",
anyDexVm())
+ .put(
+ "util.concurrent.Executors.newCachedThreadPool.Executors_newCachedThreadPool_A01",
+ match(runtimes(Runtime.ART_V5_1_1)))
.put("lang.ref.SoftReference.get.SoftReference_get_A01", cf())
.put("lang.ref.WeakReference.get.WeakReference_get_A01", cf())
.build(); // end of flakyWhenRun
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 74cff92..8ffe6af 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -981,7 +981,6 @@
LEGACY_RUNTIME))
// Dex input contains an illegal InvokeSuper in Z.foo() to Y.foo()
// that R8 will fail to compile.
- .put("594-invoke-super", TestCondition.match(TestCondition.R8DEX_COMPILER))
.put("974-verify-interface-super", TestCondition.match(TestCondition.R8DEX_COMPILER))
// R8 generates too large code in Goto.bigGoto(). b/74327727
.put("003-omnibus-opcodes", TestCondition.match(TestCondition.D8_AFTER_R8CF_COMPILER))
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index a642002..5eb1ad48 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -102,7 +102,7 @@
.withOptionConsumer(opts -> opts.enableClassInlining = false)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 115, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 103, "lambdadesugaring"))
.run();
test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
@@ -142,7 +142,7 @@
.withOptionConsumer(opts -> opts.enableClassInlining = false)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 115, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 103, "lambdadesugaring"))
.run();
test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
diff --git a/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java b/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
index 9cb139c..fd9259f 100644
--- a/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
+++ b/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
@@ -7,7 +7,7 @@
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -44,7 +44,8 @@
DirectMappedDexApplication application =
new ApplicationReader(input, options, timing).read(executorService).toDirect();
IRConverter converter =
- new IRConverter(AppView.createForR8(new AppInfoWithSubtyping(application), options), null);
+ new IRConverter(
+ AppView.createForR8(new AppInfoWithClassHierarchy(application), options), null);
converter.optimize();
DexProgramClass clazz = application.classes().iterator().next();
assertEquals(4, clazz.directMethods().size());
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index f172bb12f..7c3ddff 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -22,7 +22,6 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
@@ -35,6 +34,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.SmaliWriter;
+import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.references.ClassReference;
@@ -587,14 +587,14 @@
}
}
- protected static AppView<AppInfoWithSubtyping> computeAppViewWithSubtyping(AndroidApp app)
+ protected static AppView<AppInfoWithClassHierarchy> computeAppViewWithSubtyping(AndroidApp app)
throws Exception {
Timing timing = Timing.empty();
InternalOptions options = new InternalOptions();
DirectMappedDexApplication application =
new ApplicationReader(app, options, timing).read().toDirect();
- AppView<AppInfoWithSubtyping> appView =
- AppView.createForR8(new AppInfoWithSubtyping(application), options);
+ AppView<AppInfoWithClassHierarchy> appView =
+ AppView.createForR8(new AppInfoWithClassHierarchy(application), options);
appView.setAppServices(AppServices.builder(appView).build());
return appView;
}
@@ -616,18 +616,19 @@
Function<DexItemFactory, Collection<ProguardConfigurationRule>>
proguardConfigurationRulesGenerator)
throws Exception {
- AppView<AppInfoWithSubtyping> appView = computeAppViewWithSubtyping(app);
+ AppView<AppInfoWithClassHierarchy> appView = computeAppViewWithSubtyping(app);
// Run the tree shaker to compute an instance of AppInfoWithLiveness.
ExecutorService executor = Executors.newSingleThreadExecutor();
- DexApplication application = appView.appInfo().app();
+ DirectMappedDexApplication application = appView.appInfo().app().asDirect();
+ SubtypingInfo subtypingInfo = new SubtypingInfo(application.allClasses(), application);
RootSet rootSet =
new RootSetBuilder(
appView,
- application,
+ subtypingInfo,
proguardConfigurationRulesGenerator.apply(appView.appInfo().dexItemFactory()))
.run(executor);
AppInfoWithLiveness appInfoWithLiveness =
- EnqueuerFactory.createForInitialTreeShaking(appView)
+ EnqueuerFactory.createForInitialTreeShaking(appView, subtypingInfo)
.traceApplication(rootSet, ProguardClassFilter.empty(), executor, application.timing);
// We do not run the tree pruner to ensure that the hierarchy is as designed and not modified
// due to liveness.
@@ -1481,4 +1482,12 @@
public static String typeName(Class<?> clazz) {
return clazz.getTypeName();
}
+
+ public static AndroidApiLevel apiLevelWithDefaultInterfaceMethodsSupport() {
+ return AndroidApiLevel.N;
+ }
+
+ public static AndroidApiLevel apiLevelWithInvokeCustomSupport() {
+ return AndroidApiLevel.O;
+ }
}
diff --git a/src/test/java/com/android/tools/r8/TestBuilder.java b/src/test/java/com/android/tools/r8/TestBuilder.java
index 27af1aa..e9ac10d 100644
--- a/src/test/java/com/android/tools/r8/TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBuilder.java
@@ -12,6 +12,7 @@
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
public abstract class TestBuilder<RR extends TestRunResult, T extends TestBuilder<RR, T>> {
@@ -38,6 +39,14 @@
return self();
}
+ public T ifTrue(boolean value, Consumer<T> consumer) {
+ T self = self();
+ if (value) {
+ consumer.accept(self);
+ }
+ return self;
+ }
+
@Deprecated
public abstract RR run(String mainClass)
throws CompilationFailedException, ExecutionException, IOException;
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 080051f..5c50407 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -160,16 +160,18 @@
public static final Path D8_JAR = Paths.get(LIBS_DIR, "d8.jar");
public static final Path R8_JAR = Paths.get(LIBS_DIR, "r8.jar");
+ public static final Path R8_WITH_DEPS_JAR = Paths.get(LIBS_DIR, "r8_with_deps.jar");
public static final Path R8_WITH_RELOCATED_DEPS_JAR =
Paths.get(LIBS_DIR, "r8_with_relocated_deps.jar");
- public static final Path R8_WITH_RELOCATED_DEPS_JAR_11 =
+ public static final Path R8_WITH_DEPS_11_JAR = Paths.get(LIBS_DIR, "r8_with_deps_11.jar");
+ public static final Path R8_WITH_RELOCATED_DEPS_11_JAR =
Paths.get(LIBS_DIR, "r8_with_relocated_deps_11.jar");
public static final Path R8LIB_JAR = Paths.get(LIBS_DIR, "r8lib.jar");
public static final Path R8LIB_MAP = Paths.get(LIBS_DIR, "r8lib.jar.map");
public static final Path R8LIB_EXCLUDE_DEPS_JAR = Paths.get(LIBS_DIR, "r8lib-exclude-deps.jar");
public static final Path R8LIB_EXCLUDE_DEPS_MAP =
Paths.get(LIBS_DIR, "r8lib-exclude-deps.jar.map");
- public static final Path DEPS_NOT_RELOCATED = Paths.get(LIBS_DIR, "deps-not-relocated.jar");
+ public static final Path DEPS = Paths.get(LIBS_DIR, "deps.jar");
public static final Path DESUGAR_LIB_CONVERSIONS =
Paths.get(LIBS_DIR, "library_desugar_conversions.zip");
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java
index 11c5285..972d058 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java
@@ -205,9 +205,12 @@
for (Class clazz : CLASSES) {
ClassSubject classSubject = codeInspector.clazz(clazz);
assertThat(classSubject, isPresent());
- classSubject.getDexClass().forEachMethod(m -> {
- assertTrue(!m.isInstanceInitializer() || m.isPublicMethod());
- });
+ classSubject
+ .getDexProgramClass()
+ .forEachMethod(
+ m -> {
+ assertTrue(!m.isInstanceInitializer() || m.isPublicMethod());
+ });
}
}
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/InnerClassAttributePublicizerTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/InnerClassAttributePublicizerTest.java
index 9322844..24ba1cd 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/InnerClassAttributePublicizerTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/InnerClassAttributePublicizerTest.java
@@ -54,7 +54,7 @@
assertThat(classSubject, isPresent());
InnerClassAttribute innerClassAttribute =
- classSubject.getDexClass().getInnerClassAttributeForThisClass();
+ classSubject.getDexProgramClass().getInnerClassAttributeForThisClass();
assertNotNull(innerClassAttribute);
ClassAccessFlags accessFlags =
diff --git a/src/test/java/com/android/tools/r8/androidapi/GenerateAvailableApiExceptions.java b/src/test/java/com/android/tools/r8/androidapi/GenerateAvailableApiExceptions.java
index 83821f8..b6a1c72 100644
--- a/src/test/java/com/android/tools/r8/androidapi/GenerateAvailableApiExceptions.java
+++ b/src/test/java/com/android/tools/r8/androidapi/GenerateAvailableApiExceptions.java
@@ -92,14 +92,14 @@
if (!clazz.isPresent()) {
return false;
}
- if (clazz.getDexClass().type == inspector.getFactory().objectType) {
+ if (clazz.getDexProgramClass().type == inspector.getFactory().objectType) {
return false;
}
- if (clazz.getDexClass().type == inspector.getFactory().throwableType) {
+ if (clazz.getDexProgramClass().type == inspector.getFactory().throwableType) {
return true;
}
return cache.computeIfAbsent(
- clazz.getDexClass(),
+ clazz.getDexProgramClass(),
c -> {
return c.superType != null
&& isThrowable(inspector.clazz(c.superType.toSourceString()), cache, inspector);
diff --git a/src/test/java/com/android/tools/r8/annotations/AnnotationWithInjectedMethodsTest.java b/src/test/java/com/android/tools/r8/annotations/AnnotationWithInjectedMethodsTest.java
new file mode 100644
index 0000000..be3ee34
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/annotations/AnnotationWithInjectedMethodsTest.java
@@ -0,0 +1,176 @@
+// 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.annotations;
+
+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.enumunboxing.EnumUnboxingTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
+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 AnnotationWithInjectedMethodsTest extends EnumUnboxingTestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withAllRuntimes()
+ .withApiLevelsStartingAtIncluding(apiLevelWithDefaultInterfaceMethodsSupport())
+ .build();
+ }
+
+ public AnnotationWithInjectedMethodsTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(getProgramClasses())
+ .addProgramClassFileData(getProgramClassFileData())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(getExpectedOutput());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(getProgramClasses())
+ .addProgramClassFileData(getProgramClassFileData())
+ .addKeepMainRule(Main.class)
+ .addKeepClassAndMembersRules(AnnotationWithInjectedMethod.class)
+ .addKeepRuntimeVisibleAnnotations()
+ .addOptionsModification(options -> options.testing.allowInjectedAnnotationMethods = true)
+ .noMinification()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(getExpectedOutput());
+ }
+
+ private String getExpectedOutput() {
+ ImmutableList.Builder<String> builder = ImmutableList.builder();
+ builder.add(
+ "Foo",
+ "Bar",
+ "Caught IncompleteAnnotationException: "
+ + typeName(AnnotationWithInjectedMethod.class)
+ + " missing element getInstanceData");
+ if (parameters.isCfRuntime()) {
+ builder.add("Caught AssertionError: Too many parameters for an annotation method");
+ } else {
+ builder.add(
+ "Caught IllegalArgumentException: Invalid method for annotation type: "
+ + "public "
+ + (parameters.getRuntime().asDex().getVm().getVersion() == Version.V7_0_0
+ ? ""
+ : "default ")
+ + typeName(Data.class)
+ + " "
+ + typeName(AnnotationWithInjectedMethod.class)
+ + ".getInstanceData("
+ + typeName(Data.class)
+ + ")");
+ }
+ return StringUtils.lines(builder.build());
+ }
+
+ private Collection<Class<?>> getProgramClasses() {
+ return ImmutableList.of(Data.class);
+ }
+
+ private Collection<byte[]> getProgramClassFileData() throws IOException {
+ return ImmutableList.of(
+ transformer(Main.class)
+ .replaceAnnotationDescriptor(
+ descriptor(ToBeAnnotationWithInjectedMethod.class),
+ descriptor(AnnotationWithInjectedMethod.class))
+ .transform(),
+ transformer(AnnotationWithInjectedMethod.class)
+ .setAnnotation()
+ .removeInnerClasses()
+ .replaceAnnotationDescriptor(
+ descriptor(ToBeRetention.class), descriptor(Retention.class))
+ .transform());
+ }
+
+ static class Data {
+
+ String value;
+
+ Data(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface ToBeRetention {
+ RetentionPolicy value();
+ }
+
+ @ToBeRetention(RetentionPolicy.RUNTIME)
+ /*@*/ interface AnnotationWithInjectedMethod extends Annotation {
+
+ default Data getInstanceData() {
+ return new Data("Baz");
+ }
+
+ default Data getInstanceData(Data in) {
+ return new Data(in.value);
+ }
+
+ static Data getStaticData() {
+ return new Data("Foo");
+ }
+
+ static Data getStaticData(Data in) {
+ return new Data(in.value);
+ }
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface ToBeAnnotationWithInjectedMethod {}
+
+ @ToBeAnnotationWithInjectedMethod
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(AnnotationWithInjectedMethod.getStaticData());
+ System.out.println(AnnotationWithInjectedMethod.getStaticData(new Data("Bar")));
+
+ try {
+ System.out.println(getAnnotationWithInjectedMethod().getInstanceData());
+ } catch (Throwable e) {
+ System.out.println("Caught " + e.getClass().getSimpleName() + ": " + e.getMessage());
+ }
+
+ try {
+ System.out.println(getAnnotationWithInjectedMethod().getInstanceData(new Data("Qux")));
+ } catch (Throwable e) {
+ System.out.println("Caught " + e.getClass().getSimpleName() + ": " + e.getMessage());
+ }
+ }
+
+ static AnnotationWithInjectedMethod getAnnotationWithInjectedMethod() {
+ return Main.class.getAnnotation(AnnotationWithInjectedMethod.class);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
index 2c46b40..6a0cf4d 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
@@ -151,7 +151,7 @@
DexCode code = fooFromCls2InAbsCls.getMethod().getCode().asDexCode();
checkInstructions(code, ImmutableList.of(InvokeVirtual.class, ReturnVoid.class));
InvokeVirtual invoke = (InvokeVirtual) code.instructions[0];
- assertEquals(absSubject.getDexClass().type, invoke.getMethod().holder);
+ assertEquals(absSubject.getDexProgramClass().type, invoke.getMethod().holder);
// Cls1#foo has been moved to AbsCls#foo as a result of bridge hoisting.
MethodSubject fooInCls1 = cls1Subject.method("void", "foo", "java.lang.String");
@@ -162,7 +162,7 @@
code = fooFromCls1InAbsCls.getMethod().getCode().asDexCode();
checkInstructions(code, ImmutableList.of(InvokeVirtual.class, ReturnVoid.class));
invoke = (InvokeVirtual) code.instructions[0];
- assertEquals(absSubject.getDexClass().type, invoke.getMethod().holder);
+ assertEquals(absSubject.getDexProgramClass().type, invoke.getMethod().holder);
}
/**
@@ -259,7 +259,7 @@
DexCode code = barInCls2.getMethod().getCode().asDexCode();
checkInstructions(code, ImmutableList.of(InvokeVirtual.class, ReturnVoid.class));
InvokeVirtual invoke = (InvokeVirtual) code.instructions[0];
- assertEquals(baseSubject.getDexClass().type, invoke.getMethod().holder);
+ assertEquals(baseSubject.getDexProgramClass().type, invoke.getMethod().holder);
// Cls1#foo has been moved to Base#foo as a result of bridge hoisting.
MethodSubject fooInCls1 = cls1Subject.method("void", "foo", "java.lang.Integer");
@@ -270,7 +270,7 @@
code = fooInBase.getMethod().getCode().asDexCode();
checkInstructions(code, ImmutableList.of(InvokeVirtual.class, ReturnVoid.class));
invoke = (InvokeVirtual) code.instructions[0];
- assertEquals(baseSubject.getDexClass().type, invoke.getMethod().holder);
+ assertEquals(baseSubject.getDexProgramClass().type, invoke.getMethod().holder);
}
/**
@@ -355,7 +355,7 @@
DexCode code = barInSub.getMethod().getCode().asDexCode();
checkInstructions(code, ImmutableList.of(InvokeVirtual.class, ReturnVoid.class));
InvokeVirtual invoke = (InvokeVirtual) code.instructions[0];
- assertEquals(baseSubject.getDexClass().type, invoke.getMethod().holder);
+ assertEquals(baseSubject.getDexProgramClass().type, invoke.getMethod().holder);
}
/*
@@ -428,7 +428,7 @@
DexCode code = barInSub.getMethod().getCode().asDexCode();
checkInstructions(code, ImmutableList.of(InvokeVirtual.class, ReturnVoid.class));
InvokeVirtual invoke = (InvokeVirtual) code.instructions[0];
- assertEquals(baseSubject.getDexClass().type, invoke.getMethod().holder);
+ assertEquals(baseSubject.getDexProgramClass().type, invoke.getMethod().holder);
}
private AndroidApp runAndVerifyOnJvmAndArt(
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/NonSuperclassBridgeHoistingTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/NonSuperclassBridgeHoistingTest.java
index 353a9b9..9c4a0d6 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/NonSuperclassBridgeHoistingTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/NonSuperclassBridgeHoistingTest.java
@@ -55,7 +55,7 @@
private void inspect(CodeInspector inspector) {
ClassSubject aClassSubject = inspector.clazz(A.class);
assertThat(aClassSubject, isPresent());
- assertEquals(0, aClassSubject.getDexClass().virtualMethods().size());
+ assertEquals(0, aClassSubject.getDexProgramClass().virtualMethods().size());
ClassSubject b1ClassSubject = inspector.clazz(B.class);
assertThat(b1ClassSubject, isPresent());
diff --git a/src/test/java/com/android/tools/r8/cf/ConstantDynamicHolderTest.java b/src/test/java/com/android/tools/r8/cf/ConstantDynamicHolderTest.java
index 81f50fc..1ec23ca 100644
--- a/src/test/java/com/android/tools/r8/cf/ConstantDynamicHolderTest.java
+++ b/src/test/java/com/android/tools/r8/cf/ConstantDynamicHolderTest.java
@@ -80,7 +80,7 @@
"main",
(value, continuation) -> {
assertEquals("replaced by dynamic null constant", value);
- continuation.apply(getDynamicConstant());
+ continuation.visitLdcInsn(getDynamicConstant());
})
.transform();
}
diff --git a/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java b/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java
index 2c7d700..07a33ab 100644
--- a/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java
+++ b/src/test/java/com/android/tools/r8/cf/GetClassLdcClassTest.java
@@ -116,7 +116,11 @@
}
private static int getVersion(CodeInspector inspector, Class<?> clazz) {
- return inspector.clazz(clazz).getDexClass().asProgramClass().getInitialClassFileVersion();
+ return inspector
+ .clazz(clazz)
+ .getDexProgramClass()
+ .asProgramClass()
+ .getInitialClassFileVersion();
}
private static void checkVersion(CodeInspector inspector, Class<?> clazz, int version) {
diff --git a/src/test/java/com/android/tools/r8/cf/IdenticalCatchHandlerTest.java b/src/test/java/com/android/tools/r8/cf/IdenticalCatchHandlerTest.java
index a958c3a..e0818bb 100644
--- a/src/test/java/com/android/tools/r8/cf/IdenticalCatchHandlerTest.java
+++ b/src/test/java/com/android/tools/r8/cf/IdenticalCatchHandlerTest.java
@@ -13,9 +13,9 @@
import com.android.tools.r8.errors.Unimplemented;
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.DexCode;
import com.android.tools.r8.graph.DexCode.Try;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -71,7 +71,7 @@
private int countCatchHandlers(AndroidApp inputApp) throws Exception {
CodeInspector inspector = new CodeInspector(inputApp);
- DexClass dexClass = inspector.clazz(TestClass.class).getDexClass();
+ DexProgramClass dexClass = inspector.clazz(TestClass.class).getDexProgramClass();
Code code = dexClass.virtualMethods().get(0).getCode();
if (code.isCfCode()) {
CfCode cfCode = code.asCfCode();
diff --git a/src/test/java/com/android/tools/r8/cf/NonidenticalCatchHandlerTest.java b/src/test/java/com/android/tools/r8/cf/NonidenticalCatchHandlerTest.java
index 5d536cf..fcb172b 100644
--- a/src/test/java/com/android/tools/r8/cf/NonidenticalCatchHandlerTest.java
+++ b/src/test/java/com/android/tools/r8/cf/NonidenticalCatchHandlerTest.java
@@ -13,9 +13,9 @@
import com.android.tools.r8.errors.Unimplemented;
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.DexCode;
import com.android.tools.r8.graph.DexCode.Try;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -70,7 +70,7 @@
private int countCatchHandlers(AndroidApp inputApp) throws Exception {
CodeInspector inspector = new CodeInspector(inputApp);
- DexClass dexClass = inspector.clazz(TestClass.class).getDexClass();
+ DexProgramClass dexClass = inspector.clazz(TestClass.class).getDexProgramClass();
Code code = dexClass.virtualMethods().get(0).getCode();
if (code.isCfCode()) {
CfCode cfCode = code.asCfCode();
diff --git a/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java b/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
index f659d1c..aec1329 100644
--- a/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/TryRangeTestRunner.java
@@ -85,7 +85,7 @@
}
private void processIR(IRCode code) {
- if (!code.method.qualifiedName().equals(TryRangeTestLimitRange.class.getName() + ".main")) {
+ if (!code.method().qualifiedName().equals(TryRangeTestLimitRange.class.getName() + ".main")) {
return;
}
BasicBlock entryBlock = code.entryBlock();
diff --git a/src/test/java/com/android/tools/r8/cf/methodhandles/InvalidBootstrapMethodHandleTest.java b/src/test/java/com/android/tools/r8/cf/methodhandles/InvalidBootstrapMethodHandleTest.java
new file mode 100644
index 0000000..a815910
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/methodhandles/InvalidBootstrapMethodHandleTest.java
@@ -0,0 +1,188 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.cf.methodhandles;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeSet;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class InvalidBootstrapMethodHandleTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("Called foo!");
+
+ static final Map<String, Integer> HANDLE_TYPES =
+ ImmutableMap.<String, Integer>builder()
+ .put("GETFIELD", Opcodes.H_GETFIELD)
+ .put("GETSTATIC", Opcodes.H_GETSTATIC)
+ .put("PUTFIELD", Opcodes.H_PUTFIELD)
+ .put("PUTSTATIC", Opcodes.H_PUTSTATIC)
+ .put("INVOKEVIRTUAL", Opcodes.H_INVOKEVIRTUAL)
+ .put("INVOKESTATIC", Opcodes.H_INVOKESTATIC)
+ .put("INVOKESPECIAL", Opcodes.H_INVOKESPECIAL)
+ .put("NEWINVOKESPECIAL", Opcodes.H_NEWINVOKESPECIAL)
+ .put("INVOKEINTERFACE", Opcodes.H_INVOKEINTERFACE)
+ .build();
+
+ private final TestParameters parameters;
+ private final String handleTypeString;
+
+ @Parameterized.Parameters(name = "{0}, {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters()
+ .withAllRuntimes()
+ .withApiLevelsStartingAtIncluding(apiLevelWithInvokeCustomSupport())
+ .build(),
+ new TreeSet<>(HANDLE_TYPES.keySet()));
+ }
+
+ public InvalidBootstrapMethodHandleTest(TestParameters parameters, String handleTypeString) {
+ this.parameters = parameters;
+ this.handleTypeString = handleTypeString;
+ }
+
+ private int handleTypeOpcode() {
+ return HANDLE_TYPES.get(handleTypeString);
+ }
+
+ @Test
+ public void test() throws Exception {
+ // The compiler will fail on opcodes different from 6 and 8.
+ // Investigate why 8 is supposedly valid.
+ if (parameters.isDexRuntime()
+ && handleTypeOpcode() != Opcodes.H_INVOKESTATIC
+ && handleTypeOpcode() != Opcodes.H_NEWINVOKESPECIAL) {
+ try {
+ testForD8()
+ .addProgramClasses(InvalidBootstrapMethodHandleTestInterface.class)
+ .addProgramClassFileData(getProgramClassFileData())
+ .compileWithExpectedDiagnostics(
+ diagnotics ->
+ diagnotics.assertErrorMessageThatMatches(
+ containsString("Bootstrap handle invalid")));
+ fail("Expected compilation to fail");
+ } catch (CompilationFailedException e) {
+ // Expected failure.
+ return;
+ }
+ }
+ TestRunResult<?> result =
+ testForRuntime(parameters)
+ .addProgramClasses(InvalidBootstrapMethodHandleTestInterface.class)
+ .addProgramClassFileData(getProgramClassFileData())
+ .run(parameters.getRuntime(), InvalidBootstrapMethodHandleTestClass.class);
+ // The static target is valid and should run as expected.
+ if (handleTypeOpcode() == Opcodes.H_INVOKESTATIC) {
+ result.assertSuccessWithOutput(EXPECTED);
+ return;
+ }
+ // The invalid targets will trigger an error due to the bootstrap method not being able to be
+ // cast to the expected signature for a bootstrap method. This happens prior to invoking the
+ // target of that handle.
+ if (parameters.isCfRuntime()) {
+ result.assertFailureWithErrorThatThrows(BootstrapMethodError.class);
+ result.assertFailureWithErrorThatMatches(containsString("cannot convert"));
+ result.assertFailureWithErrorThatMatches(
+ containsString("to (Lookup,String,MethodType)Object"));
+ return;
+ }
+ // D8 allows new-invoke-special type during compilation, but it is not accepted by ART.
+ if (handleTypeOpcode() == Opcodes.H_NEWINVOKESPECIAL) {
+ result.assertFailureWithErrorThatMatches(
+ containsString("handle type is not InvokeStatic: 6"));
+ }
+ }
+
+ // Each handle is returned such that the method handle itself is valid and will not cause an
+ // error due to failed handle construction.
+ private Handle getHandle() {
+ String bootstrapSignature =
+ "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;";
+
+ switch (handleTypeOpcode()) {
+ case Opcodes.H_GETFIELD:
+ case Opcodes.H_PUTFIELD:
+ return new Handle(
+ handleTypeOpcode(),
+ binaryName(InvalidBootstrapMethodHandleTestClass.class),
+ "nonStaticField",
+ "I",
+ false);
+
+ case Opcodes.H_GETSTATIC:
+ case Opcodes.H_PUTSTATIC:
+ return new Handle(
+ handleTypeOpcode(),
+ binaryName(InvalidBootstrapMethodHandleTestClass.class),
+ "staticField",
+ "I",
+ false);
+
+ case Opcodes.H_INVOKESTATIC:
+ return new Handle(
+ handleTypeOpcode(),
+ binaryName(InvalidBootstrapMethodHandleTestClass.class),
+ "staticMethod",
+ bootstrapSignature,
+ handleTypeOpcode() == Opcodes.H_INVOKEINTERFACE);
+
+ case Opcodes.H_INVOKEVIRTUAL:
+ case Opcodes.H_INVOKESPECIAL:
+ return new Handle(
+ handleTypeOpcode(),
+ binaryName(InvalidBootstrapMethodHandleTestClass.class),
+ "virtualMethod",
+ bootstrapSignature,
+ false);
+
+ case Opcodes.H_INVOKEINTERFACE:
+ return new Handle(
+ handleTypeOpcode(),
+ binaryName(InvalidBootstrapMethodHandleTestInterface.class),
+ "virtualMethod",
+ bootstrapSignature,
+ true);
+
+ case Opcodes.H_NEWINVOKESPECIAL:
+ return new Handle(
+ handleTypeOpcode(),
+ binaryName(InvalidBootstrapMethodHandleTestClass.class),
+ "<init>",
+ "()V",
+ false);
+
+ default:
+ throw new RuntimeException("Unexpected handle type");
+ }
+ }
+
+ private byte[] getProgramClassFileData() throws Exception {
+ return transformer(InvalidBootstrapMethodHandleTestClass.class)
+ .transformMethodInsnInMethod(
+ "main",
+ (opcode, owner, name, descriptor, isInterface, visitor) -> {
+ if (opcode == Opcodes.INVOKESTATIC && name.equals("foo")) {
+ visitor.visitInvokeDynamicInsn("foo", "()V", getHandle());
+ } else {
+ visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ }
+ })
+ .transform();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/methodhandles/InvalidBootstrapMethodHandleTestClass.java b/src/test/java/com/android/tools/r8/cf/methodhandles/InvalidBootstrapMethodHandleTestClass.java
new file mode 100644
index 0000000..9040a68
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/methodhandles/InvalidBootstrapMethodHandleTestClass.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.cf.methodhandles;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+interface InvalidBootstrapMethodHandleTestInterface {
+ CallSite virtualMethod(MethodHandles.Lookup caller, String name, MethodType type)
+ throws Exception;
+}
+
+// Test data class for InvalidBootstrapMethodHandleTest.
+// Making this an inner class of the test causes changes to the VMs reflection.
+public class InvalidBootstrapMethodHandleTestClass
+ implements InvalidBootstrapMethodHandleTestInterface {
+
+ // Static field to target.
+ public static int staticField = 42;
+
+ // Non-static field to target.
+ public int nonStaticField = 42;
+
+ // Virtual method to target.
+ public CallSite virtualMethod(MethodHandles.Lookup caller, String name, MethodType type)
+ throws Exception {
+ return new ConstantCallSite(caller.findStatic(caller.lookupClass(), name, type));
+ }
+
+ // Actual valid static bootstrap method.
+ public static CallSite staticMethod(MethodHandles.Lookup caller, String name, MethodType type)
+ throws Exception {
+ return new ConstantCallSite(caller.findStatic(caller.lookupClass(), name, type));
+ }
+
+ // Constructor to target.
+ public InvalidBootstrapMethodHandleTestClass() {}
+
+ // Called by the valid virtual invoke.
+ public static void foo() {
+ System.out.println("Called foo!");
+ }
+
+ public static void main(String[] args) {
+ try {
+ // Rewritten to invoke-dynamic for each handle type.
+ foo();
+ } catch (BootstrapMethodError e) {
+ System.out.println(e.getCause().getMessage());
+ throw e;
+ }
+ }
+}
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 005df04..de3afb5 100644
--- a/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
@@ -86,7 +86,7 @@
LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
Set<String> targets = new HashSet<>();
lookupResultSuccess.forEach(
- methodTarget -> targets.add(methodTarget.asMethodTarget().getMethod().qualifiedName()),
+ methodTarget -> targets.add(methodTarget.asMethodTarget().getDefinition().qualifiedName()),
lambdaTarget -> {
assert false;
});
@@ -126,7 +126,7 @@
LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
Set<String> targets = new HashSet<>();
lookupResultSuccess.forEach(
- methodTarget -> targets.add(methodTarget.asMethodTarget().getMethod().qualifiedName()),
+ methodTarget -> targets.add(methodTarget.asMethodTarget().getDefinition().qualifiedName()),
lambdaTarget -> {
assert false;
});
@@ -165,7 +165,7 @@
LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
Set<String> targets = new HashSet<>();
lookupResultSuccess.forEach(
- methodTarget -> targets.add(methodTarget.asMethodTarget().getMethod().qualifiedName()),
+ methodTarget -> targets.add(methodTarget.asMethodTarget().getDefinition().qualifiedName()),
lambdaTarget -> {
assert false;
});
@@ -202,7 +202,7 @@
LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
Set<String> targets = new HashSet<>();
lookupResultSuccess.forEach(
- methodTarget -> targets.add(methodTarget.asMethodTarget().getMethod().qualifiedName()),
+ methodTarget -> targets.add(methodTarget.asMethodTarget().getDefinition().qualifiedName()),
lambdaTarget -> {
assert false;
});
@@ -241,7 +241,7 @@
LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
Set<String> targets = new HashSet<>();
lookupResultSuccess.forEach(
- methodTarget -> targets.add(methodTarget.asMethodTarget().getMethod().qualifiedName()),
+ methodTarget -> targets.add(methodTarget.asMethodTarget().getDefinition().qualifiedName()),
lambdaTarget -> {
assert false;
});
diff --git a/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerSuperCallInStaticTest.java b/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerSuperCallInStaticTest.java
index dafc7db..cab2a18 100644
--- a/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerSuperCallInStaticTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerSuperCallInStaticTest.java
@@ -72,7 +72,7 @@
.transformMethodInsnInMethod(
"callSuper",
(opcode, owner, name, descriptor, isInterface, continuation) -> {
- continuation.apply(
+ continuation.visitMethodInsn(
INVOKESPECIAL,
DescriptorUtils.getBinaryNameFromJavaType(Base.class.getTypeName()),
name,
diff --git a/src/test/java/com/android/tools/r8/desugar/DefaultMethodWithAccessTest.java b/src/test/java/com/android/tools/r8/desugar/DefaultMethodWithAccessTest.java
index 0c778d0..6cb4dd0 100644
--- a/src/test/java/com/android/tools/r8/desugar/DefaultMethodWithAccessTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/DefaultMethodWithAccessTest.java
@@ -51,7 +51,7 @@
.transformMethodInsnInMethod(
"access",
(opcode, owner, name, descriptor, isInterface, continuation) -> {
- continuation.apply(
+ continuation.visitMethodInsn(
name.equals("print") ? Opcodes.INVOKESPECIAL : opcode,
owner,
name,
diff --git a/src/test/java/com/android/tools/r8/desugar/PrivateMethodsInInterfaceTest.java b/src/test/java/com/android/tools/r8/desugar/PrivateMethodsInInterfaceTest.java
index 564f831..66fbdaa 100644
--- a/src/test/java/com/android/tools/r8/desugar/PrivateMethodsInInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/PrivateMethodsInInterfaceTest.java
@@ -47,7 +47,7 @@
.transformMethodInsnInMethod(
"foo",
((opcode, owner, name, descriptor, isInterface, continuation) -> {
- continuation.apply(
+ continuation.visitMethodInsn(
name.equals("bar") ? Opcodes.INVOKESPECIAL : opcode,
owner,
name,
@@ -57,7 +57,7 @@
.transformMethodInsnInMethod(
"baz",
((opcode, owner, name, descriptor, isInterface, continuation) -> {
- continuation.apply(
+ continuation.visitMethodInsn(
name.equals("bar") ? Opcodes.INVOKESPECIAL : opcode,
owner,
name,
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmulatedInterfacesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmulatedInterfacesTest.java
index 451a255..d767975 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmulatedInterfacesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/EmulatedInterfacesTest.java
@@ -84,7 +84,7 @@
}
private void assertCollectionMethodsPresentWithCorrectDispatch(CodeInspector inspector) {
- DexClass collectionDispatch = inspector.clazz("j$.util.Collection$-EL").getDexClass();
+ DexClass collectionDispatch = inspector.clazz("j$.util.Collection$-EL").getDexProgramClass();
for (DexEncodedMethod method : collectionDispatch.methods()) {
int numCheckCast =
(int)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java
index eefe0f7..03052cc 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java
@@ -4,10 +4,13 @@
package com.android.tools.r8.desugar.desugaredlibrary;
-import static org.hamcrest.core.StringContains.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -16,6 +19,7 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -39,39 +43,53 @@
@Test
public void testMergeDesugaredAndNonDesugared() throws Exception {
- D8TestCompileResult compileResult =
- testForD8()
- .addProgramFiles(buildPart1DesugaredLibrary(), buildPart2NoDesugaredLibrary())
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
- .setMinApi(parameters.getApiLevel())
- .enableCoreLibraryDesugaring(parameters.getApiLevel())
- .compile()
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibrary, parameters.getApiLevel());
- // TODO(b/154106502): This should raise a proper warning. The dex files are incompatible,
- // so the behavior is undefined regarding desugared types.
- if (parameters.getApiLevel().getLevel() < AndroidApiLevel.N.getLevel()) {
- compileResult
- .run(parameters.getRuntime(), Part1.class)
- .assertSuccessWithOutputLines(J$_RESULT);
- } else {
- compileResult
- .run(parameters.getRuntime(), Part1.class)
- .assertSuccessWithOutputLines(JAVA_RESULT);
+ D8TestCompileResult compileResult;
+ try {
+ compileResult =
+ testForD8()
+ .addProgramFiles(buildPart1DesugaredLibrary(), buildPart2NoDesugaredLibrary())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(this::assertError);
+ assertFalse(expectError());
+ } catch (CompilationFailedException e) {
+ assertTrue(expectError());
+ return;
}
- if (parameters.getRuntime().asDex().getMinApiLevel().getLevel()
- < AndroidApiLevel.N.getLevel()) {
- compileResult
- .run(parameters.getRuntime(), Part2.class)
- .assertFailureWithErrorThatMatches(containsString("java.lang.NoSuchMethodError"));
- } else {
+ assert !expectError();
+ assert compileResult != null;
+ compileResult.addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary, parameters.getApiLevel());
+ compileResult
+ .run(parameters.getRuntime(), Part1.class)
+ .assertSuccessWithOutputLines(JAVA_RESULT);
+ compileResult
+ .run(parameters.getRuntime(), Part2.class)
+ .assertSuccessWithOutputLines(JAVA_RESULT);
+ }
- compileResult
- .run(parameters.getRuntime(), Part2.class)
- .assertSuccessWithOutputLines(JAVA_RESULT);
+ private void assertError(TestDiagnosticMessages m) {
+ List<Diagnostic> errors = m.getErrors();
+ if (expectError()) {
+ assertEquals(1, errors.size());
+ assertTrue(
+ errors.stream()
+ .anyMatch(
+ w ->
+ w.getDiagnosticMessage()
+ .contains(
+ "The compilation is merging inputs with different"
+ + " desugared library desugaring")));
+ } else {
+ assertEquals(0, errors.size());
}
}
+ private boolean expectError() {
+ return parameters.getApiLevel().getLevel() <= AndroidApiLevel.N.getLevel();
+ }
+
@Test
public void testMergeDesugaredAndClassFile() throws Exception {
D8TestCompileResult compileResult =
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
index 835d1cb..83dc73e 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/ConversionIntroduceInterfaceMethodTest.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
-import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
@@ -99,12 +98,12 @@
"com.android.tools.r8.desugar.desugaredlibrary.conversiontests")
&& !c.getOriginalName().contains("Executor")
&& !c.getOriginalName().contains("$-CC")
- && !c.getDexClass().isInterface())
+ && !c.getDexProgramClass().isInterface())
.collect(toSingle());
assertEquals(
"Missing duplicated forEach",
2,
- myCollection.getDexClass().virtualMethods().stream()
+ myCollection.getDexProgramClass().virtualMethods().stream()
.filter(m -> m.method.name.toString().equals("forEach"))
.count());
}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/FullNestOnProgramPathTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/FullNestOnProgramPathTest.java
index 55f40c4..e4447b53 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/FullNestOnProgramPathTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/FullNestOnProgramPathTest.java
@@ -168,7 +168,7 @@
ImmutableList.of("NeverInline", "OutsideInliningNoAccess", "OutsideInliningWithAccess");
inspector.forAllClasses(
classSubject -> {
- DexClass dexClass = classSubject.getDexClass();
+ DexClass dexClass = classSubject.getDexProgramClass();
if (!nonNestClasses.contains(dexClass.type.getName())) {
assertTrue(dexClass.isInANest());
if (outerClassNames.contains(dexClass.type.getName())) {
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11D8CompilationTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11D8CompilationTest.java
index 5c8018e..6fdfe97 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11D8CompilationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11D8CompilationTest.java
@@ -32,13 +32,14 @@
}
private static void assertNoNests(CodeInspector inspector) {
- assertTrue(inspector.allClasses().stream().noneMatch(subj -> subj.getDexClass().isInANest()));
+ assertTrue(
+ inspector.allClasses().stream().noneMatch(subj -> subj.getDexProgramClass().isInANest()));
}
@Test
public void testR8CompiledWithD8() throws Exception {
testForD8()
- .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR_11)
+ .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR)
.compile()
.inspect(Java11D8CompilationTest::assertNoNests);
}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
index 1f6832c..5a788bf 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
@@ -64,7 +64,7 @@
private static Path compileR8(boolean desugar) throws Exception {
// Shrink R8 11 with R8
return testForR8(TestBase.getStaticTemp(), Backend.CF)
- .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR_11)
+ .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR)
.addKeepRuleFiles(MAIN_KEEP)
.addOptionsModification(
options -> options.testing.enableForceNestBasedAccessDesugaringForTest = desugar)
@@ -75,16 +75,18 @@
private static void assertNests(CodeInspector inspector, boolean desugar) {
if (desugar) {
- assertTrue(inspector.allClasses().stream().noneMatch(subj -> subj.getDexClass().isInANest()));
+ assertTrue(
+ inspector.allClasses().stream().noneMatch(subj -> subj.getDexProgramClass().isInANest()));
} else {
- assertTrue(inspector.allClasses().stream().anyMatch(subj -> subj.getDexClass().isInANest()));
+ assertTrue(
+ inspector.allClasses().stream().anyMatch(subj -> subj.getDexProgramClass().isInANest()));
}
}
private Path[] jarsToCompare() {
return new Path[] {
ToolHelper.R8_WITH_RELOCATED_DEPS_JAR,
- ToolHelper.R8_WITH_RELOCATED_DEPS_JAR_11,
+ ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR,
r8Lib11NoDesugar,
r8Lib11Desugar
};
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
index a79ccf7..762916e 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
@@ -41,14 +41,15 @@
private static final Path MAIN_KEEP = Paths.get("src/main/keep.txt");
private static void assertNoNests(CodeInspector inspector) {
- assertTrue(inspector.allClasses().stream().noneMatch(subj -> subj.getDexClass().isInANest()));
+ assertTrue(
+ inspector.allClasses().stream().noneMatch(subj -> subj.getDexProgramClass().isInANest()));
}
@Test
public void testR8CompiledWithR8() throws Exception {
testForR8(parameters.getBackend())
.setMinApi(parameters.getApiLevel())
- .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR_11)
+ .addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR)
.addKeepRuleFiles(MAIN_KEEP)
.addOptionsModification(opt -> opt.ignoreMissingClasses = true)
.allowDiagnosticWarningMessages()
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
index e0c7e12..816a233 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
@@ -107,7 +107,7 @@
public static void assertNestAttributesCorrect(CodeInspector inspector) {
assertTrue(inspector.allClasses().size() > 0);
for (FoundClassSubject classSubject : inspector.allClasses()) {
- DexClass clazz = classSubject.getDexClass();
+ DexClass clazz = classSubject.getDexProgramClass();
if (clazz.isInANest()) {
if (clazz.isNestHost()) {
// All members are present with the clazz as host
@@ -116,8 +116,8 @@
ClassSubject inner = inspector.clazz(PACKAGE_NAME + memberName);
assertNotNull(
"The nest member " + memberName + " of " + clazz.type.getName() + " is missing",
- inner.getDexClass());
- assertSame(inner.getDexClass().getNestHost(), clazz.type);
+ inner.getDexProgramClass());
+ assertSame(inner.getDexProgramClass().getNestHost(), clazz.type);
}
} else {
// Nest host is present and with the clazz as member
@@ -125,9 +125,9 @@
ClassSubject host = inspector.clazz(PACKAGE_NAME + hostName);
assertNotNull(
"The nest host " + hostName + " of " + clazz.type.getName() + " is missing",
- host.getDexClass());
+ host.getDexProgramClass());
assertTrue(
- host.getDexClass().getNestMembersClassAttributes().stream()
+ host.getDexProgramClass().getNestMembersClassAttributes().stream()
.anyMatch(attr -> attr.getNestMember() == clazz.type));
}
}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithStaticResolutionInvokeVirtualTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithStaticResolutionInvokeVirtualTest.java
index a8c5c6f..e2474ca 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithStaticResolutionInvokeVirtualTest.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithStaticResolutionInvokeVirtualTest.java
@@ -64,14 +64,14 @@
(opcode, owner, name, descriptor, isInterface, continuation) -> {
if (invalidInvoke && opcode == Opcodes.INVOKEVIRTUAL) {
assertEquals("m", name);
- continuation.apply(
+ continuation.visitMethodInsn(
opcode,
DescriptorUtils.getBinaryNameFromJavaType(C.class.getTypeName()),
name,
descriptor,
isInterface);
} else {
- continuation.apply(opcode, owner, name, descriptor, isInterface);
+ continuation.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
})
.transform());
diff --git a/src/test/java/com/android/tools/r8/dexfilemerger/DexMergeChecksumsFileWithNoClassesTest.java b/src/test/java/com/android/tools/r8/dexfilemerger/DexMergeChecksumsFileWithNoClassesTest.java
index 57fe4b3..c987370 100644
--- a/src/test/java/com/android/tools/r8/dexfilemerger/DexMergeChecksumsFileWithNoClassesTest.java
+++ b/src/test/java/com/android/tools/r8/dexfilemerger/DexMergeChecksumsFileWithNoClassesTest.java
@@ -65,7 +65,7 @@
inspector.getMarkers().forEach(m -> assertTrue(m.getHasChecksums()));
// It may be prudent to check that the dex file also has the encoding string, but that is
// not easily accessed.
- inspector.allClasses().forEach(c -> c.getDexClass().asProgramClass().getChecksum());
+ inspector.allClasses().forEach(c -> c.getDexProgramClass().asProgramClass().getChecksum());
}
public static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/dexfilemerger/NonAsciiClassNameChecksumTest.java b/src/test/java/com/android/tools/r8/dexfilemerger/NonAsciiClassNameChecksumTest.java
index 8e4700b..b91a645 100644
--- a/src/test/java/com/android/tools/r8/dexfilemerger/NonAsciiClassNameChecksumTest.java
+++ b/src/test/java/com/android/tools/r8/dexfilemerger/NonAsciiClassNameChecksumTest.java
@@ -83,7 +83,7 @@
private void checkIncludesChecksum(CodeInspector inspector, Class<?> clazz) {
ClassSubject classSubject = inspector.clazz(getTransformedName(clazz));
assertThat(classSubject, isPresent());
- assertTrue(classSubject.getDexClass().asProgramClass().getChecksum() > 0);
+ assertTrue(classSubject.getDexProgramClass().asProgramClass().getChecksum() > 0);
}
static class TaestClass {
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterInlineRegression.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterInlineRegression.java
index 14d1e63..8f6a2d6 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterInlineRegression.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterInlineRegression.java
@@ -4,7 +4,10 @@
package com.android.tools.r8.dexsplitter;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -16,13 +19,13 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ThrowingConsumer;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
-import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
-import java.util.function.Predicate;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -46,15 +49,11 @@
@Test
public void testInliningFromFeature() throws Exception {
- Predicate<R8TestCompileResult> ensureGetFromFeatureGone =
+ ThrowingConsumer<R8TestCompileResult, Exception> ensureGetFromFeatureGone =
r8TestCompileResult -> {
// Ensure that getFromFeature from FeatureClass is inlined into the run method.
- try {
- ClassSubject clazz = r8TestCompileResult.inspector().clazz(FeatureClass.class);
- return clazz.uniqueMethodWithName("getFromFeature").isAbsent();
- } catch (IOException | ExecutionException ex) {
- throw new RuntimeException("Found getFromFeature in FeatureClass");
- }
+ ClassSubject clazz = r8TestCompileResult.inspector().clazz(FeatureClass.class);
+ assertThat(clazz.uniqueMethodWithName("getFromFeature"), not(isPresent()));
};
Consumer<R8FullTestBuilder> configurator =
r8FullTestBuilder -> r8FullTestBuilder.enableMergeAnnotations().noMinification();
@@ -83,8 +82,7 @@
ImmutableSet.of(BaseSuperClass.class),
ImmutableSet.of(FeatureClass.class),
FeatureClass.class,
- EXPECTED,
- a -> true,
+ ConsumerUtils.emptyThrowingConsumer(),
configurator);
assertEquals(processResult.exitCode, 0);
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMemberValuePropagationRegression.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMemberValuePropagationRegression.java
new file mode 100644
index 0000000..fa3ae88
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMemberValuePropagationRegression.java
@@ -0,0 +1,114 @@
+// 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.dexsplitter;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.ConsumerUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class DexSplitterMemberValuePropagationRegression extends SplitterTestBase {
+
+ public static final String EXPECTED = StringUtils.lines("42");
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection params() {
+ return getTestParameters().withAllRuntimes().build();
+ }
+
+ private final TestParameters parameters;
+
+ public DexSplitterMemberValuePropagationRegression(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testPropagationFromFeature() throws Exception {
+ ThrowingConsumer<R8TestCompileResult, Exception> ensureGetFromFeatureGone =
+ r8TestCompileResult -> {
+ // Ensure that getFromFeature from FeatureClass is inlined into the run method.
+ ClassSubject clazz = r8TestCompileResult.inspector().clazz(FeatureClass.class);
+ assertThat(clazz.uniqueMethodWithName("getFromFeature"), not(isPresent()));
+ };
+ ProcessResult processResult =
+ testDexSplitter(
+ parameters,
+ ImmutableSet.of(BaseSuperClass.class),
+ ImmutableSet.of(FeatureClass.class, FeatureEnum.class),
+ FeatureClass.class,
+ EXPECTED,
+ ensureGetFromFeatureGone,
+ builder -> builder.enableInliningAnnotations().noMinification());
+ // We expect art to fail on this with the dex splitter, see b/122902374
+ assertNotEquals(processResult.exitCode, 0);
+ assertTrue(processResult.stderr.contains("NoClassDefFoundError"));
+ }
+
+ @Test
+ public void testOnR8Splitter() throws IOException, CompilationFailedException {
+ assumeTrue(parameters.isDexRuntime());
+ ProcessResult processResult =
+ testR8Splitter(
+ parameters,
+ ImmutableSet.of(BaseSuperClass.class),
+ ImmutableSet.of(FeatureClass.class, FeatureEnum.class),
+ FeatureClass.class,
+ ConsumerUtils.emptyThrowingConsumer(),
+ R8TestBuilder::enableInliningAnnotations);
+ assertEquals(processResult.exitCode, 0);
+ assertEquals(processResult.stdout, EXPECTED);
+ }
+
+ public abstract static class BaseSuperClass implements RunInterface {
+
+ @NeverInline
+ @Override
+ public void run() {
+ System.out.println(getFromFeature());
+ }
+
+ public abstract Enum<?> getFromFeature();
+ }
+
+ public static class FeatureClass extends BaseSuperClass {
+
+ @NeverInline
+ @Override
+ public Enum<?> getFromFeature() {
+ return FeatureEnum.A;
+ }
+ }
+
+ public enum FeatureEnum {
+ A;
+
+ @Override
+ public String toString() {
+ return "42";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
index 25cbe9a..067f920 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
@@ -4,13 +4,14 @@
package com.android.tools.r8.dexsplitter;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NeverMerge;
import com.android.tools.r8.R8FullTestBuilder;
@@ -18,13 +19,13 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ThrowingConsumer;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
-import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
-import java.util.function.Predicate;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -49,16 +50,12 @@
@Test
public void testInliningFromFeature() throws Exception {
// Static merging is based on sorting order, we assert that we merged to the feature.
- Predicate<R8TestCompileResult> ensureMergingToFeature =
+ ThrowingConsumer<R8TestCompileResult, Exception> ensureMergingToFeature =
r8TestCompileResult -> {
- try {
- ClassSubject clazz = r8TestCompileResult.inspector().clazz(AFeatureWithStatic.class);
- return clazz.allMethods().size() == 2
- && clazz.uniqueMethodWithName("getBase42").isPresent()
- && clazz.uniqueMethodWithName("getFoobar").isPresent();
- } catch (IOException | ExecutionException ex) {
- throw new RuntimeException("Failed lookup up AFeatureWithStatic");
- }
+ ClassSubject clazz = r8TestCompileResult.inspector().clazz(AFeatureWithStatic.class);
+ assertEquals(2, clazz.allMethods().size());
+ assertThat(clazz.uniqueMethodWithName("getBase42"), isPresent());
+ assertThat(clazz.uniqueMethodWithName("getFoobar"), isPresent());
};
Consumer<R8FullTestBuilder> configurator =
r8FullTestBuilder ->
@@ -82,8 +79,7 @@
}
@Test
- public void testOnR8Splitter() throws IOException, CompilationFailedException,
- ExecutionException {
+ public void testOnR8Splitter() throws IOException, CompilationFailedException {
assumeTrue(parameters.isDexRuntime());
Consumer<R8FullTestBuilder> configurator =
r8FullTestBuilder -> r8FullTestBuilder.enableMergeAnnotations().noMinification();
@@ -93,8 +89,7 @@
ImmutableSet.of(BaseClass.class, BaseWithStatic.class),
ImmutableSet.of(FeatureClass.class, AFeatureWithStatic.class),
FeatureClass.class,
- EXPECTED,
- a -> true,
+ ConsumerUtils.emptyThrowingConsumer(),
configurator);
assertEquals(processResult.exitCode, 0);
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/R8SplitterInlineToFeature.java b/src/test/java/com/android/tools/r8/dexsplitter/R8SplitterInlineToFeature.java
new file mode 100644
index 0000000..331403a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dexsplitter/R8SplitterInlineToFeature.java
@@ -0,0 +1,98 @@
+// 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.dexsplitter;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.google.common.collect.ImmutableSet;
+import java.util.function.Consumer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * We don't allow to inline code from the base into a feature, but we do allow inlining code from
+ * the the base into the feature.
+ */
+@RunWith(Parameterized.class)
+public class R8SplitterInlineToFeature extends SplitterTestBase {
+
+ public static final String EXPECTED = StringUtils.lines("42");
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection params() {
+ return getTestParameters().withDexRuntimes().build();
+ }
+
+ private final TestParameters parameters;
+
+ public R8SplitterInlineToFeature(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testInlining() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ Consumer<R8FullTestBuilder> configurator =
+ r8FullTestBuilder -> r8FullTestBuilder.enableMergeAnnotations().noMinification();
+ ThrowingConsumer<R8TestCompileResult, Exception> ensureInlined =
+ r8TestCompileResult -> {
+ // Ensure that isEarly from BaseUtilClass is inlined into the feature
+ ClassSubject clazz = r8TestCompileResult.inspector().clazz(BaseUtilClass.class);
+ assertThat(clazz.uniqueMethodWithName("isEarly"), not(isPresent()));
+ };
+ ProcessResult processResult =
+ testR8Splitter(
+ parameters,
+ ImmutableSet.of(BaseSuperClass.class, BaseUtilClass.class),
+ ImmutableSet.of(FeatureClass.class),
+ FeatureClass.class,
+ ensureInlined,
+ configurator);
+
+ assertEquals(processResult.exitCode, 0);
+ assertEquals(processResult.stdout, StringUtils.lines("42"));
+ }
+
+ @NeverMerge
+ public abstract static class BaseSuperClass implements RunInterface {
+ public void run() {
+ System.out.println(getFromFeature());
+ }
+
+ public abstract String getFromFeature();
+ }
+
+ public static class BaseUtilClass {
+ public static boolean isEarly() {
+ return System.currentTimeMillis() < 2;
+ }
+ }
+
+ public static class FeatureClass extends BaseSuperClass {
+ @Override
+ public String getFromFeature() {
+ if (BaseUtilClass.isEarly()) {
+ return "Very early";
+ } else {
+ return "42";
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java b/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
index 1e31022..16291c4 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/SplitterTestBase.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.dexsplitter.DexSplitter.Options;
import com.android.tools.r8.utils.ArchiveResourceProvider;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.ThrowingConsumer;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
@@ -32,7 +33,6 @@
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
-import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
@@ -121,20 +121,19 @@
TemporaryFolder temp,
Collection<String> nonJavaFiles,
boolean ensureClassesInOutput,
- Class... classes) {
+ Class<?>... classes) {
addConsumers(builder, outputPath, temp, nonJavaFiles, true, Arrays.asList(classes));
return builder.build();
}
- protected ProcessResult testR8Splitter(
+ protected <E extends Throwable> ProcessResult testR8Splitter(
TestParameters parameters,
Set<Class<?>> baseClasses,
Set<Class<?>> featureClasses,
- Class toRun,
- String expectedOutput,
- Predicate<R8TestCompileResult> predicate,
+ Class<?> toRun,
+ ThrowingConsumer<R8TestCompileResult, E> compileResultConsumer,
Consumer<R8FullTestBuilder> r8TestConfigurator)
- throws IOException, CompilationFailedException {
+ throws IOException, CompilationFailedException, E {
Path featureOutput = temp.newFile("feature.zip").toPath();
R8FullTestBuilder r8FullTestBuilder = testForR8(parameters.getBackend());
@@ -158,7 +157,7 @@
r8TestConfigurator.accept(r8FullTestBuilder);
R8TestCompileResult r8TestCompileResult = r8FullTestBuilder.compile();
- assertTrue(predicate.test(r8TestCompileResult));
+ compileResultConsumer.accept(r8TestCompileResult);
Path baseOutput = r8TestCompileResult.writeToZip();
return runFeatureOnArt(toRun, baseOutput, featureOutput, parameters.getRuntime());
@@ -166,15 +165,15 @@
// Compile the passed in classes plus RunInterface and SplitRunner using R8, then split
// based on the base/feature sets. toRun must implement the BaseRunInterface
- protected ProcessResult testDexSplitter(
+ protected <E extends Throwable> ProcessResult testDexSplitter(
TestParameters parameters,
Set<Class<?>> baseClasses,
Set<Class<?>> featureClasses,
- Class toRun,
+ Class<?> toRun,
String expectedOutput,
- Predicate<R8TestCompileResult> predicate,
+ ThrowingConsumer<R8TestCompileResult, E> compileResultConsumer,
Consumer<R8FullTestBuilder> r8TestConfigurator)
- throws Exception {
+ throws Exception, E {
List<Class<?>> baseClassesWithRunner =
ImmutableList.<Class<?>>builder()
.add(RunInterface.class, SplitRunner.class)
@@ -229,7 +228,7 @@
.addKeepClassRules(toRun);
r8TestConfigurator.accept(r8FullTestBuilder);
R8TestCompileResult r8TestCompileResult = r8FullTestBuilder.compile();
- assertTrue(predicate.test(r8TestCompileResult));
+ compileResultConsumer.accept(r8TestCompileResult);
Path fullFiles = r8TestCompileResult.writeToZip();
// Ensure that we can run the program as a unit (i.e., without splitting)
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java
index 7cc0930..1cf4e93 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/AnnotationEnumUnboxingTest.java
@@ -5,7 +5,10 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestParameters;
+import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
@@ -41,16 +44,14 @@
enumKeepRules.toString().equals("none"));
testForR8(parameters.getBackend())
.addInnerClasses(AnnotationEnumUnboxingTest.class)
+ .noMinification()
.addKeepMainRule(Main.class)
.addKeepRules(enumKeepRules.getKeepRule())
- .addKeepRules(
- "-keep @interface"
- + " com.android.tools.r8.enumunboxing."
- + "AnnotationEnumUnboxingTest$ClassAnnotationDefault"
- + " { }",
- "-keepattributes *Annotation*")
+ .addKeepClassRules(ClassAnnotationDefault.class)
+ .addKeepRuntimeVisibleAnnotations()
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
+ .enableMergeAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.allowDiagnosticInfoMessages()
.setMinApi(parameters.getApiLevel())
@@ -62,9 +63,13 @@
assertEnumIsBoxed(
MyEnumArrayDefault.class, MyEnumArrayDefault.class.getSimpleName(), m);
assertEnumIsBoxed(MyEnumArray.class, MyEnumArray.class.getSimpleName(), m);
+ assertEnumIsUnboxed(
+ MyEnumRetMethod2.class, MyEnumRetMethod2.class.getSimpleName(), m);
+ assertEnumIsUnboxed(
+ MyEnumParamMethod2.class, MyEnumParamMethod2.class.getSimpleName(), m);
})
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("print", "1", "1", "1", "1", "1", "0", "0", "0");
+ .assertSuccessWithOutputLines("print", "1", "1", "1", "1", "1", "0", "0", "0", "0");
}
@Retention(RetentionPolicy.RUNTIME)
@@ -78,6 +83,20 @@
MyEnumArray[] myEnumArray();
}
+ @NeverMerge
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface ClassAnnotation {}
+
+ enum MyEnumParamMethod2 {
+ A,
+ B
+ }
+
+ enum MyEnumRetMethod2 {
+ A,
+ B
+ }
+
enum MyEnumDefault {
A,
B
@@ -111,6 +130,20 @@
}
}
+ static class ClassAnnotationSub implements ClassAnnotation {
+
+ @NeverInline
+ @NeverPropagateValue
+ MyEnumRetMethod2 enumMethod(MyEnumParamMethod2 param) {
+ return param == MyEnumParamMethod2.A ? MyEnumRetMethod2.A : MyEnumRetMethod2.B;
+ }
+
+ @Override
+ public Class<? extends Annotation> annotationType() {
+ return null;
+ }
+ }
+
static class Main {
public static void main(String[] args) {
new ClassDefault().print();
@@ -124,6 +157,7 @@
System.out.println(annotation.myEnumArray()[0].ordinal());
System.out.println(annotation.myEnumArrayDefault()[0].ordinal());
System.out.println(annotation.myEnumDefault().ordinal());
+ System.out.println(new ClassAnnotationSub().enumMethod(MyEnumParamMethod2.A).ordinal());
}
}
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumClinitWithSideEffectsUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumClinitWithSideEffectsUnboxingTest.java
index 2fbd235..c3e548e 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumClinitWithSideEffectsUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumClinitWithSideEffectsUnboxingTest.java
@@ -1,7 +1,6 @@
package com.android.tools.r8.enumunboxing;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.InternalOptions;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,7 +30,6 @@
.addInnerClasses(EnumClinitWithSideEffectsUnboxingTest.class)
.addKeepMainRule(TestClass.class)
.addKeepRules(enumKeepRule.getKeepRule())
- .addOptionsModification(InternalOptions::enableEnumUnboxing)
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumInitWithSideEffectsUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumInitWithSideEffectsUnboxingTest.java
index d353692..b9445c2 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumInitWithSideEffectsUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumInitWithSideEffectsUnboxingTest.java
@@ -1,7 +1,6 @@
package com.android.tools.r8.enumunboxing;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.InternalOptions;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,7 +30,6 @@
.addInnerClasses(EnumInitWithSideEffectsUnboxingTest.class)
.addKeepMainRule(TestClass.class)
.addKeepRules(enumKeepRule.getKeepRule())
- .addOptionsModification(InternalOptions::enableEnumUnboxing)
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingTest.java
index ad16ee1..0441cbf 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingTest.java
@@ -82,12 +82,13 @@
}
private void assertEnumsAsExpected(CodeInspector inspector) {
- assertEquals(1, inspector.clazz(EnumInterface.class).getDexClass().interfaces.size());
+ assertEquals(1, inspector.clazz(EnumInterface.class).getDexProgramClass().interfaces.size());
assertTrue(inspector.clazz(EnumStaticField.class).uniqueFieldWithName("X").isPresent());
assertTrue(inspector.clazz(EnumInstanceField.class).uniqueFieldWithName("a").isPresent());
- assertEquals(5, inspector.clazz(EnumStaticMethod.class).getDexClass().directMethods().size());
+ assertEquals(
+ 5, inspector.clazz(EnumStaticMethod.class).getDexProgramClass().directMethods().size());
assertEquals(1, inspector.clazz(EnumVirtualMethod.class).virtualMethods().size());
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java
index 8e7be57..411a747 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java
@@ -85,9 +85,10 @@
// Check all as expected (else we test nothing)
assertEquals(
- 1, inspector.clazz(InstanceFieldPutObject.class).getDexClass().instanceFields().size());
+ 1,
+ inspector.clazz(InstanceFieldPutObject.class).getDexProgramClass().instanceFields().size());
assertEquals(
- 1, inspector.clazz(StaticFieldPutObject.class).getDexClass().staticFields().size());
+ 1, inspector.clazz(StaticFieldPutObject.class).getDexProgramClass().staticFields().size());
assertTrue(inspector.clazz(FailingPhi.class).uniqueMethodWithName("switchOn").isPresent());
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingTest.java
index eedc8f1..8ab4bcf 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FieldPutEnumUnboxingTest.java
@@ -56,9 +56,10 @@
.inspect(
i -> {
assertEquals(
- 1, i.clazz(InstanceFieldPut.class).getDexClass().instanceFields().size());
+ 1,
+ i.clazz(InstanceFieldPut.class).getDexProgramClass().instanceFields().size());
assertEquals(
- 1, i.clazz(StaticFieldPut.class).getDexClass().staticFields().size());
+ 1, i.clazz(StaticFieldPut.class).getDexProgramClass().staticFields().size());
});
for (Class<?> input : INPUTS) {
diff --git a/src/test/java/com/android/tools/r8/graph/DexTypeTest.java b/src/test/java/com/android/tools/r8/graph/DexTypeTest.java
index 1b5ffa9..014249b 100644
--- a/src/test/java/com/android/tools/r8/graph/DexTypeTest.java
+++ b/src/test/java/com/android/tools/r8/graph/DexTypeTest.java
@@ -19,7 +19,7 @@
public class DexTypeTest {
private static DexItemFactory factory;
- private static AppInfoWithSubtyping appInfo;
+ private static AppInfoWithClassHierarchy appInfo;
@BeforeClass
public static void makeAppInfo() throws Exception {
@@ -35,7 +35,7 @@
.read()
.toDirect();
factory = options.itemFactory;
- appInfo = new AppInfoWithSubtyping(application);
+ appInfo = new AppInfoWithClassHierarchy(application);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
index 088b075..5001645 100644
--- a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
@@ -101,7 +101,7 @@
//
// class <T:GenericSignatureTestClassA<T>.Y>CYY<T extends A<T>.Y> extends CY<T>
- DexClass clazz = cyy.getDexClass();
+ DexClass clazz = cyy.getDexProgramClass();
assertNotNull(clazz);
classSignature = Parser.toClassSignature(clazz, appView);
assertNotNull(classSignature);
@@ -112,14 +112,14 @@
assertNull(formalTypeParameter.interfaceBounds);
assertTrue(formalTypeParameter.classBound.isClassTypeSignature());
ClassTypeSignature classBoundSignature = formalTypeParameter.classBound.asClassTypeSignature();
- assertEquals(y.getDexClass().type, classBoundSignature.innerTypeSignature.type);
+ assertEquals(y.getDexProgramClass().type, classBoundSignature.innerTypeSignature.type);
assertEquals(1, classBoundSignature.typeArguments.size());
assertEquals(
"T", classBoundSignature.typeArguments.get(0).asTypeVariableSignature().typeVariable);
assertTrue(classSignature.superInterfaceSignatures.isEmpty());
classTypeSignature = classSignature.superClassSignature;
- assertEquals(cy.getDexClass().type, classTypeSignature.type);
+ assertEquals(cy.getDexProgramClass().type, classTypeSignature.type);
typeArguments = classTypeSignature.typeArguments;
assertEquals(1, typeArguments.size());
typeArgument = typeArguments.get(0);
@@ -159,13 +159,13 @@
FormalTypeParameter methodFormalParameter = methodTypeSignature.formalTypeParameters.get(0);
assertTrue(methodFormalParameter.classBound.isClassTypeSignature());
assertEquals(
- y.getDexClass().getType(),
+ y.getDexProgramClass().getType(),
methodFormalParameter.classBound.asClassTypeSignature().innerTypeSignature.type);
assertNotNull(methodFormalParameter.interfaceBounds);
assertEquals(1, methodFormalParameter.interfaceBounds.size());
FieldTypeSignature interfaceBound = methodFormalParameter.interfaceBounds.get(0);
assertTrue(interfaceBound.isClassTypeSignature());
- assertEquals(i.getDexClass().getType(), interfaceBound.asClassTypeSignature().type);
+ assertEquals(i.getDexProgramClass().getType(), interfaceBound.asClassTypeSignature().type);
// return type: A$Y$YY
returnType = methodTypeSignature.returnType();
@@ -186,7 +186,7 @@
assertTrue(elementSignature.isFieldTypeSignature());
assertTrue(elementSignature.asFieldTypeSignature().isClassTypeSignature());
classTypeSignature = elementSignature.asFieldTypeSignature().asClassTypeSignature();
- assertEquals(b.getDexClass().type, classTypeSignature.type);
+ assertEquals(b.getDexProgramClass().type, classTypeSignature.type);
// Function<A$Y$ZZ<TT>, A$Y$YY> convertToYY(Supplier<A$Y$ZZ<TT>>
MethodSubject convertToYY = zz.uniqueMethodWithName("convertToYY");
@@ -246,26 +246,27 @@
}
private void check_A_Y(ClassSubject a, ClassSubject y, ClassTypeSignature signature) {
- assertEquals(a.getDexClass().type, signature.type);
+ assertEquals(a.getDexProgramClass().type, signature.type);
List<FieldTypeSignature> typeArguments = signature.typeArguments;
assertEquals(1, typeArguments.size());
FieldTypeSignature typeArgument = typeArguments.get(0);
assertTrue(typeArgument.isTypeVariableSignature());
assertEquals("T", typeArgument.asTypeVariableSignature().typeVariable);
- assertEquals(y.getDexClass().type, signature.innerTypeSignature.type);
+ assertEquals(y.getDexProgramClass().type, signature.innerTypeSignature.type);
}
private void check_A_Y_YY(
ClassSubject a, ClassSubject y, ClassSubject yy, ClassTypeSignature signature) {
check_A_Y(a, y, signature);
- assertEquals(yy.getDexClass().type, signature.innerTypeSignature.innerTypeSignature.type);
+ assertEquals(
+ yy.getDexProgramClass().type, signature.innerTypeSignature.innerTypeSignature.type);
}
private void check_A_Y_ZZ(
ClassSubject a, ClassSubject y, ClassSubject zz, ClassTypeSignature signature) {
check_A_Y(a, y, signature);
ClassTypeSignature innerMost = signature.innerTypeSignature.innerTypeSignature;
- assertEquals(zz.getDexClass().type, innerMost.type);
+ assertEquals(zz.getDexProgramClass().type, innerMost.type);
List<FieldTypeSignature> typeArguments = innerMost.typeArguments;
assertEquals(1, typeArguments.size());
FieldTypeSignature typeArgument = typeArguments.get(0);
diff --git a/src/test/java/com/android/tools/r8/graph/InvokeFinalTest.java b/src/test/java/com/android/tools/r8/graph/InvokeFinalTest.java
index f72a98f..54f2fa4 100644
--- a/src/test/java/com/android/tools/r8/graph/InvokeFinalTest.java
+++ b/src/test/java/com/android/tools/r8/graph/InvokeFinalTest.java
@@ -63,7 +63,7 @@
(opcode, owner, name, descriptor, isInterface, continuation) -> {
// The super call to bar() is already INVOKESPECIAL.
assertTrue(name.equals("foo") || opcode == INVOKESPECIAL);
- continuation.apply(INVOKESPECIAL, owner, name, descriptor, isInterface);
+ continuation.visitMethodInsn(INVOKESPECIAL, owner, name, descriptor, isInterface);
})
.transform();
}
diff --git a/src/test/java/com/android/tools/r8/graph/InvokeSuperTest.java b/src/test/java/com/android/tools/r8/graph/InvokeSuperTest.java
index f71538c..1abfea6f 100644
--- a/src/test/java/com/android/tools/r8/graph/InvokeSuperTest.java
+++ b/src/test/java/com/android/tools/r8/graph/InvokeSuperTest.java
@@ -3,12 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -114,48 +111,42 @@
SubClassOfInvokerClass.class)
.addProgramClassFileData(InvokerClassDump.dumpNonVerifying())
.run(parameters.getRuntime(), MainClassFailing.class)
- .apply(this::checkNonVerifyingResult);
+ .apply(r -> checkNonVerifyingResult(r, false));
}
- private void checkNonVerifyingResult(TestRunResult<?> result) {
+ private void checkNonVerifyingResult(TestRunResult<?> result, boolean isR8) {
// The input is invalid and any JVM will fail at verification time.
if (parameters.isCfRuntime()) {
- result.assertFailureWithErrorThatMatches(containsString(VerifyError.class.getName()));
+ result.assertFailureWithErrorThatThrows(VerifyError.class);
return;
}
- // D8 cannot verify its inputs and the behavior of the compiled output differs.
- // The failure is due to lambda desugaring on pre-7 and fails at runtime on 7+.
+ // Dex results vary wildly...
Version version = parameters.getRuntime().asDex().getVm().getVersion();
- if (version.isOlderThanOrEqual(Version.V6_0_1)) {
- result.assertFailureWithErrorThatMatches(
- allOf(containsString("java.lang.NoClassDefFoundError"), containsString("-$$Lambda$")));
- return;
+ if (!isR8 && version.isOlderThanOrEqual(Version.V4_4_4)) {
+ result.assertFailureWithErrorThatThrows(VerifyError.class);
+ } else if (version == Version.V5_1_1 || version == Version.V6_0_1) {
+ result.assertFailure();
+ } else {
+ result.assertSuccessWithOutputThatMatches(containsString(NoSuchMethodError.class.getName()));
}
- result.assertSuccessWithOutputLines(NoSuchMethodError.class.getName());
}
@Test
public void testR8NonVerifying() throws Exception {
- try {
- testForR8(parameters.getBackend())
- .addProgramClasses(
- MainClassFailing.class,
- Consumer.class,
- Super.class,
- SubLevel1.class,
- SubLevel2.class,
- SubClassOfInvokerClass.class)
- .addProgramClassFileData(InvokerClassDump.dumpNonVerifying())
- .setMinApi(parameters.getApiLevel())
- .addKeepMainRule(MainClassFailing.class)
- .compileWithExpectedDiagnostics(
- diagnostics -> {
- diagnostics.assertErrorMessageThatMatches(containsString("Illegal invoke-super"));
- });
- fail("Expected compilation to fail");
- } catch (CompilationFailedException e) {
- // Expected compilation failure.
- }
+ testForR8(parameters.getBackend())
+ .addProgramClasses(
+ MainClassFailing.class,
+ Consumer.class,
+ Super.class,
+ SubLevel1.class,
+ SubLevel2.class,
+ SubClassOfInvokerClass.class)
+ .addProgramClassFileData(InvokerClassDump.dumpNonVerifying())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(MainClassFailing.class)
+ .addOptionsModification(o -> o.testing.allowTypeErrors = true)
+ .run(parameters.getRuntime(), MainClassFailing.class)
+ .apply(r -> checkNonVerifyingResult(r, true));
}
/** Copy of {@ref java.util.function.Consumer} to allow tests to run on early versions of art. */
@@ -253,7 +244,7 @@
static class MainClassFailing {
- private static void tryInvoke(java.util.function.Consumer<InvokerClass> function) {
+ private static void tryInvoke(Consumer<InvokerClass> function) {
InvokerClass invoker = new InvokerClass();
try {
function.accept(invoker);
diff --git a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
index 2438ab2..8524677 100644
--- a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -261,7 +261,7 @@
assertEquals(mOnI1, appInfo.lookupSuperTarget(mOnI1, c1).method);
assertEquals(mOnI2, appInfo.lookupSuperTarget(mOnI2, c1).method);
- assertEquals(mOnI0, appInfo.lookupSuperTarget(mOnC1, c2).method);
+ assertNull(appInfo.lookupSuperTarget(mOnC1, c2)); // C2 is not a subclass of C1.
assertEquals(mOnI1, appInfo.lookupSuperTarget(mOnI3, c2).method);
assertEquals(mOnI2, appInfo.lookupSuperTarget(mOnI4, c2).method);
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialForInvokeVirtualTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialForInvokeVirtualTest.java
index 283f479..eaf7d56 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialForInvokeVirtualTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialForInvokeVirtualTest.java
@@ -51,7 +51,7 @@
"bar",
(opcode, owner, name, descriptor, isInterface, continuation) -> {
assertEquals(INVOKEVIRTUAL, opcode);
- continuation.apply(INVOKESPECIAL, owner, name, descriptor, isInterface);
+ continuation.visitMethodInsn(INVOKESPECIAL, owner, name, descriptor, isInterface);
})
.transform();
}
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialForNonDeclaredInvokeVirtualTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialForNonDeclaredInvokeVirtualTest.java
index 1477df4..9b9a8eb 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialForNonDeclaredInvokeVirtualTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialForNonDeclaredInvokeVirtualTest.java
@@ -51,7 +51,7 @@
"bar",
(opcode, owner, name, descriptor, isInterface, continuation) -> {
assertEquals(INVOKEVIRTUAL, opcode);
- continuation.apply(INVOKESPECIAL, owner, name, descriptor, isInterface);
+ continuation.visitMethodInsn(INVOKESPECIAL, owner, name, descriptor, isInterface);
})
.transform();
}
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceTest.java
index 24ae2fc..7a51803 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceTest.java
@@ -63,7 +63,7 @@
"bar",
(opcode, owner, name, descriptor, isInterface, continuation) -> {
assertEquals(INVOKEVIRTUAL, opcode);
- continuation.apply(INVOKESPECIAL, owner, name, descriptor, isInterface);
+ continuation.visitMethodInsn(INVOKESPECIAL, owner, name, descriptor, isInterface);
})
.transform();
}
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridgeTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridgeTest.java
index 647881d..3536adb 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridgeTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridgeTest.java
@@ -53,7 +53,7 @@
(opcode, owner, name, descriptor, isInterface, continuation) -> {
assertEquals(INVOKEVIRTUAL, opcode);
assertEquals(owner, DescriptorUtils.getBinaryNameFromJavaType(B.class.getTypeName()));
- continuation.apply(INVOKESPECIAL, owner, name, descriptor, isInterface);
+ continuation.visitMethodInsn(INVOKESPECIAL, owner, name, descriptor, isInterface);
})
.transform();
}
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialMissingInvokeVirtualTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialMissingInvokeVirtualTest.java
index 5343c5a..93a94e9 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialMissingInvokeVirtualTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialMissingInvokeVirtualTest.java
@@ -54,7 +54,7 @@
(opcode, owner, name, descriptor, isInterface, continuation) -> {
assertEquals(INVOKEVIRTUAL, opcode);
assertEquals("notify", name);
- continuation.apply(
+ continuation.visitMethodInsn(
INVOKESPECIAL,
DescriptorUtils.getBinaryNameFromJavaType(A.class.getTypeName()),
"foo",
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnSameClassTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnSameClassTest.java
index 7fae808..5bc6d22 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnSameClassTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnSameClassTest.java
@@ -56,7 +56,7 @@
.transformMethodInsnInMethod(
"bar",
(opcode, owner, name, descriptor, isInterface, continuation) -> {
- continuation.apply(INVOKESPECIAL, owner, name, descriptor, isInterface);
+ continuation.visitMethodInsn(INVOKESPECIAL, owner, name, descriptor, isInterface);
})
.transform();
}
diff --git a/src/test/java/com/android/tools/r8/graph/invokestatic/InvokeStaticOnInterfaceTest.java b/src/test/java/com/android/tools/r8/graph/invokestatic/InvokeStaticOnInterfaceTest.java
index ecdaa6f..090844e 100644
--- a/src/test/java/com/android/tools/r8/graph/invokestatic/InvokeStaticOnInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokestatic/InvokeStaticOnInterfaceTest.java
@@ -99,7 +99,7 @@
(opcode, owner, name, descriptor, isInterface, continuation) -> {
assertEquals(INVOKESTATIC, opcode);
assertTrue(isInterface);
- continuation.apply(opcode, owner, name, descriptor, false);
+ continuation.visitMethodInsn(opcode, owner, name, descriptor, false);
})
.transform();
}
diff --git a/src/test/java/com/android/tools/r8/internal/NestTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/NestTreeShakeJarVerificationTest.java
index 8166511..2c06bd8 100644
--- a/src/test/java/com/android/tools/r8/internal/NestTreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/NestTreeShakeJarVerificationTest.java
@@ -26,7 +26,8 @@
null,
ImmutableList.of(BASE + DEPLOY_JAR));
assertEquals(0, filterKotlinMetadata(handler.warnings).count());
- assertEquals(0, filterKotlinMetadata(handler.infos).count());
+ // TODO(b/155536535): We find bad descriptors. See if we can still resolve them.
+ assertEquals(2, filterKotlinMetadata(handler.infos).count());
}
private Stream<Diagnostic> filterKotlinMetadata(List<Diagnostic> warnings) {
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 183a37e..ccbfd3a 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
@@ -13,7 +13,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -41,7 +41,7 @@
private static final String APP_DIR = "third_party/gmscore/v5/";
private DirectMappedDexApplication program;
- private AppView<? extends AppInfoWithSubtyping> appView;
+ private AppView<? extends AppInfoWithClassHierarchy> appView;
private SubtypingInfo subtypingInfo;
@Before
@@ -60,12 +60,12 @@
.read(proguardMap, executorService)
.toDirect();
InternalOptions options = new InternalOptions();
- appView = AppView.createForR8(new AppInfoWithSubtyping(program), options);
+ appView = AppView.createForR8(new AppInfoWithClassHierarchy(program), options);
appView.setAppServices(AppServices.builder(appView).build());
subtypingInfo = new SubtypingInfo(program.allClasses(), program);
}
- private AppInfoWithSubtyping appInfo() {
+ private AppInfoWithClassHierarchy appInfo() {
return appView.appInfo();
}
@@ -106,7 +106,7 @@
Counter counter = new Counter();
lookupResult.forEach(
target -> {
- DexEncodedMethod m = target.getMethod();
+ DexEncodedMethod m = target.getDefinition();
if (m.accessFlags.isAbstract() || !m.accessFlags.isBridge()) {
counter.inc();
}
@@ -117,7 +117,7 @@
Counter counter = new Counter();
lookupResult.forEach(
target -> {
- if (target.getMethod().isAbstract()) {
+ if (target.getDefinition().isAbstract()) {
counter.inc();
}
},
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
index 23dc574..8c98b46 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
@@ -186,7 +186,8 @@
}
private void verifyMethodToInvokeValuesAreAbsent(CodeInspector outputInspector) {
- DexType methodToInvokeType = outputInspector.clazz(METHOD_TO_INVOKE_ENUM).getDexClass().type;
+ DexType methodToInvokeType =
+ outputInspector.clazz(METHOD_TO_INVOKE_ENUM).getDexProgramClass().type;
for (String main : mains) {
MethodSubject mainMethodSubject = outputInspector.clazz(main).mainMethod();
assertThat(mainMethodSubject, isPresent());
diff --git a/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java b/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java
index 60975b5..3c3aee6 100644
--- a/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java
+++ b/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java
@@ -59,11 +59,11 @@
assertThat(clazz, isPresent());
// There are two direct methods, but only because one is <init>.
- assertEquals(2, clazz.getDexClass().directMethods().size());
+ assertEquals(2, clazz.getDexProgramClass().directMethods().size());
assertThat(clazz.method("void", "<init>", ImmutableList.of()), isPresent());
// There is only one virtual method.
- assertEquals(1, clazz.getDexClass().virtualMethods().size());
+ assertEquals(1, clazz.getDexProgramClass().virtualMethods().size());
}
@Test
@@ -94,7 +94,7 @@
assertThat(clazz, isPresent());
// Redundant fields have been removed.
- assertEquals(1, clazz.getDexClass().instanceFields().size());
- assertEquals(1, clazz.getDexClass().staticFields().size());
+ assertEquals(1, clazz.getDexProgramClass().instanceFields().size());
+ assertEquals(1, clazz.getDexProgramClass().staticFields().size());
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/InlineTest.java b/src/test/java/com/android/tools/r8/ir/InlineTest.java
index a2b9dce..a2246c0 100644
--- a/src/test/java/com/android/tools/r8/ir/InlineTest.java
+++ b/src/test/java/com/android/tools/r8/ir/InlineTest.java
@@ -6,11 +6,13 @@
import static org.junit.Assert.assertEquals;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
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.DexItemFactory;
+import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -47,18 +49,20 @@
MethodSubject method,
List<IRCode> additionalCode)
throws ExecutionException {
- AppView<AppInfoWithSubtyping> appView =
- AppView.createForR8(new AppInfoWithSubtyping(application.asDirect()), options);
+ DirectMappedDexApplication directApp = application.asDirect();
+ AppView<AppInfoWithClassHierarchy> appView =
+ AppView.createForR8(new AppInfoWithClassHierarchy(directApp), options);
appView.setAppServices(AppServices.builder(appView).build());
ExecutorService executorService = ThreadUtils.getExecutorService(options);
+ SubtypingInfo subtypingInfo = new SubtypingInfo(directApp.allClasses(), directApp);
appView.setRootSet(
new RootSetBuilder(
appView,
- application,
+ subtypingInfo,
ImmutableList.of(ProguardKeepRule.defaultKeepAllRule(unused -> {})))
.run(executorService));
Timing timing = Timing.empty();
- Enqueuer enqueuer = EnqueuerFactory.createForInitialTreeShaking(appView);
+ Enqueuer enqueuer = EnqueuerFactory.createForInitialTreeShaking(appView, subtypingInfo);
appView.setAppInfo(
enqueuer.traceApplication(
appView.rootSet(), ProguardClassFilter.empty(), executorService, timing));
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/AnalysisTestBase.java b/src/test/java/com/android/tools/r8/ir/analysis/AnalysisTestBase.java
index ebfb64f..80fef97 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/AnalysisTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/AnalysisTestBase.java
@@ -8,15 +8,10 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.util.function.Consumer;
@@ -28,7 +23,6 @@
protected final TestParameters parameters;
private final AndroidApp app;
private final String className;
- private final InternalOptions options = new InternalOptions();
public AppView<?> appView;
@@ -64,9 +58,7 @@
@Before
public void setup() throws Exception {
- DirectMappedDexApplication application =
- new ApplicationReader(app, options, Timing.empty()).read().toDirect();
- appView = AppView.createForR8(new AppInfoWithSubtyping(application), options);
+ appView = computeAppViewWithLiveness(app);
}
public void buildAndCheckIR(String methodName, Consumer<IRCode> irInspector) {
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
index 43c8e17..bf823d4 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
@@ -16,7 +16,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -80,7 +80,7 @@
@Test
public void testOptimizationInfo() throws Exception {
- AppView<AppInfoWithSubtyping> appView = buildApp();
+ AppView<AppInfoWithClassHierarchy> appView = buildApp();
OptimizationFeedbackMock feedback = new OptimizationFeedbackMock();
FieldBitAccessAnalysis fieldBitAccessAnalysis = new FieldBitAccessAnalysis();
FieldAccessAnalysis fieldAccessAnalysis =
@@ -114,7 +114,7 @@
}
}
- private AppView<AppInfoWithSubtyping> buildApp() throws IOException, ExecutionException {
+ private AppView<AppInfoWithClassHierarchy> buildApp() throws IOException, ExecutionException {
DexItemFactory dexItemFactory = new DexItemFactory();
InternalOptions options = new InternalOptions(dexItemFactory, new Reporter());
options.programConsumer =
@@ -133,7 +133,7 @@
timing)
.read()
.toDirect();
- return AppView.createForR8(new AppInfoWithSubtyping(application), options);
+ return AppView.createForR8(new AppInfoWithClassHierarchy(application), options);
}
private DexEncodedField uniqueFieldByName(DexProgramClass clazz, String name) {
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/NarrowingWithoutSubtypingTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/NarrowingWithoutSubtypingTest.java
index 2feb99a..6644af2 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/NarrowingWithoutSubtypingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/NarrowingWithoutSubtypingTest.java
@@ -20,7 +20,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withDexRuntimes().build();
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
}
public NarrowingWithoutSubtypingTest(TestParameters parameters) {
@@ -36,7 +36,7 @@
options.testing.enableNarrowingChecksInD8 = true;
options.testing.noLocalsTableOnInput = true;
})
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutputLines("Hello world!");
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
index 5d2814c..cfe27c9 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/TypeLatticeTest.java
@@ -15,7 +15,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
@@ -39,7 +39,7 @@
private static final String INTERRUPT = "Ljava/io/InterruptedIOException;";
private static DexItemFactory factory;
- private static AppView<AppInfoWithSubtyping> appView;
+ private static AppView<AppInfoWithClassHierarchy> appView;
@BeforeClass
public static void makeAppInfo() throws Exception {
@@ -60,7 +60,7 @@
.read()
.toDirect();
factory = options.itemFactory;
- appView = AppView.createForR8(new AppInfoWithSubtyping(application), options);
+ appView = AppView.createForR8(new AppInfoWithClassHierarchy(application), options);
}
private TopTypeElement top() {
diff --git a/src/test/java/com/android/tools/r8/ir/conversion/PartialCallGraphTest.java b/src/test/java/com/android/tools/r8/ir/conversion/PartialCallGraphTest.java
index 1955c47..1ea4e67 100644
--- a/src/test/java/com/android/tools/r8/ir/conversion/PartialCallGraphTest.java
+++ b/src/test/java/com/android/tools/r8/ir/conversion/PartialCallGraphTest.java
@@ -10,21 +10,15 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
-import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.ir.conversion.CallGraph.Node;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.Enqueuer;
-import com.android.tools.r8.shaking.EnqueuerFactory;
import com.android.tools.r8.shaking.ProguardConfigurationParser;
-import com.android.tools.r8.shaking.RootSetBuilder;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
@@ -36,33 +30,24 @@
public class PartialCallGraphTest extends CallGraphTestBase {
private final AppView<AppInfoWithLiveness> appView;
- private final InternalOptions options = new InternalOptions();
- private final ExecutorService executorService = ThreadUtils.getExecutorService(options);
+ private final InternalOptions options;
+ private final ExecutorService executorService;
public PartialCallGraphTest() throws Exception {
- Timing timing = Timing.empty();
AndroidApp app = testForD8().addProgramClasses(TestClass.class).compile().app;
- DirectMappedDexApplication application =
- new ApplicationReader(app, options, timing).read().toDirect();
- AppView<AppInfoWithSubtyping> appView =
- AppView.createForR8(new AppInfoWithSubtyping(application), options);
- appView.setAppServices(AppServices.builder(appView).build());
- ProguardConfigurationParser parser =
- new ProguardConfigurationParser(appView.dexItemFactory(), options.reporter);
- parser.parse(
- createConfigurationForTesting(
- ImmutableList.of("-keep class ** { void m1(); void m5(); }")));
- appView.setRootSet(
- new RootSetBuilder(
- appView, application, parser.getConfig().getRules()).run(executorService));
- Enqueuer enqueuer = EnqueuerFactory.createForInitialTreeShaking(appView);
this.appView =
- appView.setAppInfo(
- enqueuer.traceApplication(
- appView.rootSet(),
- parser.getConfig().getDontWarnPatterns(),
- executorService,
- timing));
+ computeAppViewWithLiveness(
+ app,
+ factory -> {
+ ProguardConfigurationParser parser =
+ new ProguardConfigurationParser(factory, new Reporter());
+ parser.parse(
+ createConfigurationForTesting(
+ ImmutableList.of("-keep class ** { void m1(); void m5(); }")));
+ return parser.getConfig().getRules();
+ });
+ this.options = appView.options();
+ this.executorService = ThreadUtils.getExecutorService(options);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ConstraintWithTargetTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ConstraintWithTargetTest.java
index 41eba43..81f2ba5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/ConstraintWithTargetTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ConstraintWithTargetTest.java
@@ -5,37 +5,45 @@
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.Timing;
import org.junit.BeforeClass;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
-public class ConstraintWithTargetTest {
+@RunWith(Parameterized.class)
+public class ConstraintWithTargetTest extends TestBase {
+
private static DexItemFactory factory;
- private static AppView<AppInfoWithSubtyping> appView;
+ private static AppView<AppInfoWithLiveness> appView;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ private final TestParameters parameters;
+
+ public ConstraintWithTargetTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
@BeforeClass
public static void makeAppInfo() throws Exception {
- InternalOptions options = new InternalOptions();
- DirectMappedDexApplication application =
- new ApplicationReader(
- AndroidApp.builder().addLibraryFiles(ToolHelper.getDefaultAndroidJar()).build(),
- options,
- Timing.empty())
- .read()
- .toDirect();
- factory = options.itemFactory;
- appView = AppView.createForR8(new AppInfoWithSubtyping(application), options);
+ AndroidApp app = AndroidApp.builder().addLibraryFiles(ToolHelper.getJava8RuntimeJar()).build();
+ appView = computeAppViewWithLiveness(app);
+ factory = appView.dexItemFactory();
}
private ConstraintWithTarget never() {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java
index 15456be..28ab813 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DirectMappedDexApplication;
@@ -22,7 +22,8 @@
InternalOptions options = new InternalOptions();
DirectMappedDexApplication dexApplication =
new ApplicationReader(app, options, timing).read().toDirect();
- AppView<?> appView = AppView.createForD8(new AppInfoWithSubtyping(dexApplication), options);
+ AppView<?> appView =
+ AppView.createForD8(new AppInfoWithClassHierarchy(dexApplication), options);
appView.setAppServices(AppServices.builder(appView).build());
return appView;
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index 49718d4..5daa849 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -374,11 +374,7 @@
.getMethod()
.name
.toString()
- .equals(EnumUnboxingRewriter.ENUM_UNBOXING_UTILITY_ORDINAL)
- && !((InvokeInstructionSubject) instruction)
- .holder()
- .toString()
- .contains("java.lang.Enum")) {
+ .equals(EnumUnboxingRewriter.ENUM_UNBOXING_UTILITY_ORDINAL)) {
++invokeCount;
}
}
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
new file mode 100644
index 0000000..1a2d667
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/CallSiteOptimizationWithInvokeCustomTargetTest.java
@@ -0,0 +1,132 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.callsites;
+
+import static com.android.tools.r8.references.Reference.methodFromMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.google.common.collect.ImmutableList;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class CallSiteOptimizationWithInvokeCustomTargetTest extends TestBase {
+
+ private static final String EXPECTED = StringUtils.lines("Hello world!");
+
+ 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();
+ }
+
+ public CallSiteOptimizationWithInvokeCustomTargetTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClassFileData(getProgramClassFileData())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(getProgramClassFileData())
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMethodRules(methodFromMethod(TestClass.class.getDeclaredMethod("bar", int.class)))
+ .enableInliningAnnotations()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(
+ inspector -> {
+ ClassSubject clazz = inspector.clazz(TestClass.class);
+ assertThat(clazz.uniqueMethodWithName("bootstrap"), isPresent());
+ assertThat(clazz.uniqueMethodWithName("bar"), isPresent());
+ assertThat(clazz.uniqueMethodWithName("foo"), not(isPresent()));
+ });
+ }
+
+ private List<byte[]> getProgramClassFileData() throws Exception {
+ return ImmutableList.of(
+ transformer(TestClass.class)
+ .transformMethodInsnInMethod(
+ "main",
+ (opcode, owner, name, descriptor, isInterface, visitor) -> {
+ if (opcode == Opcodes.INVOKESTATIC && name.equals("foo")) {
+ visitor.visitInvokeDynamicInsn(
+ "foo",
+ "(I)V",
+ new Handle(
+ Opcodes.H_INVOKESTATIC,
+ binaryName(TestClass.class),
+ "bootstrap",
+ "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;",
+ false));
+ } else {
+ visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ }
+ })
+ .transform());
+ }
+
+ static class TestClass {
+
+ @NeverInline
+ static CallSite bootstrap(MethodHandles.Lookup lookup, String unused, MethodType type)
+ throws NoSuchMethodException, IllegalAccessException {
+ return lookup != null
+ ? new ConstantCallSite(
+ // Reflective access of bar, needs a keep rule.
+ lookup.findStatic(TestClass.class, "bar", type))
+ : null;
+ }
+
+ // Target of the bootstrap method.
+ static void bar(int i) {
+ if (i == 42) {
+ System.out.println("Hello world!");
+ }
+ }
+
+ // Placeholder. Never called.
+ static void foo(int i) {
+ throw null;
+ }
+
+ public static void main(String[] args) throws Exception {
+ // Direct call to the bootstrap method with constant arguments, triggering call-site opt.
+ bootstrap(null, null, null);
+ // Rewritten to invoke-dynamic foo(I)V, bsm:TestClass::bootstrap
+ TestClass.foo(args.length + 42);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/PropagationFromSiblingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/PropagationFromSiblingTest.java
new file mode 100644
index 0000000..fb3bc2e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/PropagationFromSiblingTest.java
@@ -0,0 +1,89 @@
+// 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.optimize.callsites;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+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.Parameters;
+
+@RunWith(Parameterized.class)
+public class PropagationFromSiblingTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public PropagationFromSiblingTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(PropagationFromSiblingTest.class)
+ .addKeepMainRule(TestClass.class)
+ .addOptionsModification(options -> options.enableUnusedInterfaceRemoval = false)
+ .enableInliningAnnotations()
+ .enableMergeAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ getJAsI().m(null); // The null argument is not propagated to A.m() in 2.0.59.
+ new B().m("Hello world!");
+ }
+
+ @NeverInline
+ static I getJAsI() {
+ if (System.currentTimeMillis() > 0) {
+ return new B();
+ }
+ return new C();
+ }
+ }
+
+ interface I {
+ void m(Object o);
+ }
+
+ interface J extends I {}
+
+ @NeverMerge
+ static class A implements I {
+
+ @NeverInline
+ @Override
+ public void m(Object o) {
+ if (o != null) {
+ System.out.println(o.toString());
+ }
+ }
+ }
+
+ @NeverClassInline
+ static class B extends A implements J {}
+
+ static class C implements J {
+
+ @Override
+ public void m(Object o) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/StaticFinalLibraryFieldCanonicalizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/StaticFinalLibraryFieldCanonicalizationTest.java
index 7da9e81..e46efd5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/StaticFinalLibraryFieldCanonicalizationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/StaticFinalLibraryFieldCanonicalizationTest.java
@@ -10,13 +10,11 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.TestParametersCollection;
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 com.google.common.collect.ImmutableSet;
-import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -26,17 +24,13 @@
public class StaticFinalLibraryFieldCanonicalizationTest extends TestBase {
private final TestParameters parameters;
- private final boolean systemHasClassInitializationSideEffects;
- @Parameters(name = "{0}, System has class initialization side effects: {1}")
- public static List<Object[]> data() {
- return buildParameters(
- getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- public StaticFinalLibraryFieldCanonicalizationTest(
- TestParameters parameters, boolean systemHasClassInitializationSideEffects) {
- this.systemHasClassInitializationSideEffects = systemHasClassInitializationSideEffects;
+ public StaticFinalLibraryFieldCanonicalizationTest(TestParameters parameters) {
this.parameters = parameters;
}
@@ -45,18 +39,11 @@
testForR8(parameters.getBackend())
.addProgramClasses(TestClass.class)
.addKeepMainRule(TestClass.class)
- .addOptionsModification(
- options -> {
- if (!systemHasClassInitializationSideEffects) {
- options.itemFactory.libraryClassesWithoutStaticInitialization =
- ImmutableSet.of(options.itemFactory.createType("Ljava/lang/System;"));
- }
- })
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
.run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutputLines("Hello world!");
+ .assertSuccessWithOutputLines("true", "true");
}
private void inspect(CodeInspector inspector) {
@@ -66,15 +53,20 @@
MethodSubject mainMethodSubject = testClassSubject.mainMethod();
assertThat(mainMethodSubject, isPresent());
assertEquals(
- systemHasClassInitializationSideEffects || parameters.isCfRuntime() ? 2 : 1,
- mainMethodSubject.streamInstructions().filter(InstructionSubject::isStaticGet).count());
+ 1,
+ mainMethodSubject
+ .streamInstructions()
+ .filter(InstructionSubject::isStaticGet)
+ .map(InstructionSubject::getField)
+ .filter(inspector.getFactory().booleanMembers.TRUE::equals)
+ .count());
}
static class TestClass {
public static void main(String[] args) {
- System.out.print("Hello");
- System.out.println(" world!");
+ System.out.println(Boolean.TRUE);
+ System.out.println(Boolean.TRUE);
}
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/commonsubexpressionelimination/TrivialPhiAfterCommonSubexpressionEliminationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/commonsubexpressionelimination/TrivialPhiAfterCommonSubexpressionEliminationTest.java
new file mode 100644
index 0000000..c238466
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/commonsubexpressionelimination/TrivialPhiAfterCommonSubexpressionEliminationTest.java
@@ -0,0 +1,48 @@
+// 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.optimize.commonsubexpressionelimination;
+
+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;
+
+@RunWith(Parameterized.class)
+public class TrivialPhiAfterCommonSubexpressionEliminationTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public TrivialPhiAfterCommonSubexpressionEliminationTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(TrivialPhiAfterCommonSubexpressionEliminationTest.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("42");
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ int x = args.length;
+ int y = x + 42;
+ int z = x + 42;
+ System.out.println(System.currentTimeMillis() > 0 ? y : z);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ifs/ConstClassComparisonTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ifs/ConstClassComparisonTest.java
new file mode 100644
index 0000000..6b8cb68
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ifs/ConstClassComparisonTest.java
@@ -0,0 +1,83 @@
+// 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.ifs;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+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.Parameters;
+
+@RunWith(Parameterized.class)
+public class ConstClassComparisonTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ConstClassComparisonTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(ConstClassComparisonTest.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("true", "false", "false", "true");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz(TestClass.class);
+ assertThat(classSubject, isPresent());
+
+ MethodSubject mainMethod = classSubject.mainMethod();
+ assertThat(mainMethod, isPresent());
+ assertTrue(mainMethod.streamInstructions().noneMatch(InstructionSubject::isConstClass));
+
+ assertThat(inspector.clazz(A.class), not(isPresent()));
+ assertThat(inspector.clazz(B.class), not(isPresent()));
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ System.out.println(getA() == getA());
+ System.out.println(getA() != getA());
+ System.out.println(getA() == getB());
+ System.out.println(getA() != getB());
+ }
+
+ static Class<?> getA() {
+ return A.class;
+ }
+
+ static Class<?> getB() {
+ return B.class;
+ }
+ }
+
+ static class A {}
+
+ static class B {}
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ifs/EnumAliasComparisonTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ifs/EnumAliasComparisonTest.java
index 5c0f75b..aa60b25 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/ifs/EnumAliasComparisonTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ifs/EnumAliasComparisonTest.java
@@ -44,7 +44,7 @@
.compile()
.inspect(this::inspect)
.run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutputLines("true");
+ .assertSuccessWithOutputLines("true", "false", "false", "true", "true", "false");
}
private void inspect(CodeInspector inspector) {
@@ -67,15 +67,20 @@
static class TestClass {
public static void main(String[] args) {
- // Should print "true" since MyEnum.B is an alias of MyEnum.A.
+ System.out.println(MyEnum.A == MyEnum.A);
+ System.out.println(MyEnum.A != MyEnum.A);
System.out.println(MyEnum.A == MyEnum.B);
+ System.out.println(MyEnum.A != MyEnum.B);
+ System.out.println(MyEnum.A == MyEnum.C);
+ System.out.println(MyEnum.A != MyEnum.C);
}
}
enum MyEnum {
- A;
+ A,
+ B;
// Introduce an alias of MyEnum.A.
- static MyEnum B = A;
+ static MyEnum C = A;
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineCatchHandlerWithLibraryTypeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineCatchHandlerWithLibraryTypeTest.java
index 15e37c1..5874af4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineCatchHandlerWithLibraryTypeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineCatchHandlerWithLibraryTypeTest.java
@@ -82,7 +82,7 @@
type.equals(TEMPLATE_CODE_EXCEPTION_BINARY_NAME)
? getExceptionBinaryName()
: type;
- continuation.apply(start, end, handler, newType);
+ continuation.visitTryCatchBlock(start, end, handler, newType);
})
.transform();
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/logging/AndroidLogRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/logging/AndroidLogRemovalTest.java
index 1314274..0031ca2 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/logging/AndroidLogRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/logging/AndroidLogRemovalTest.java
@@ -51,7 +51,7 @@
.transformMethodInsnInMethod(
"main",
(opcode, owner, name, descriptor, isInterface, continuation) ->
- continuation.apply(
+ continuation.visitMethodInsn(
opcode,
owner.endsWith("$Log") ? "android/util/Log" : owner,
name,
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/singleton/SingletonFieldValuePropagationEnumTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/singleton/SingletonFieldValuePropagationEnumTest.java
index 1463a01..6fbed5e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/singleton/SingletonFieldValuePropagationEnumTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/singleton/SingletonFieldValuePropagationEnumTest.java
@@ -5,8 +5,8 @@
package com.android.tools.r8.ir.optimize.membervaluepropagation.fields.singleton;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -45,9 +45,7 @@
private void inspect(CodeInspector inspector) {
ClassSubject charactersClassSubject = inspector.clazz(Characters.class);
- assertThat(charactersClassSubject, isPresent());
- // TODO(b/150368955): Field value propagation should cause Characters.value to become dead.
- assertEquals(1, charactersClassSubject.allInstanceFields().size());
+ assertThat(charactersClassSubject, not(isPresent()));
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/singleton/SingletonFieldValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/singleton/SingletonFieldValuePropagationTest.java
index 371844e..3c78f04 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/singleton/SingletonFieldValuePropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/singleton/SingletonFieldValuePropagationTest.java
@@ -5,8 +5,8 @@
package com.android.tools.r8.ir.optimize.membervaluepropagation.fields.singleton;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -45,9 +45,7 @@
private void inspect(CodeInspector inspector) {
ClassSubject charactersClassSubject = inspector.clazz(Characters.class);
- assertThat(charactersClassSubject, isPresent());
- // TODO(b/150368955): Field value propagation should cause Characters.value to become dead.
- assertEquals(1, charactersClassSubject.allInstanceFields().size());
+ assertThat(charactersClassSubject, not(isPresent()));
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b111893131/B111893131.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b111893131/B111893131.java
index cebcd05..ed54024 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b111893131/B111893131.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b111893131/B111893131.java
@@ -89,7 +89,7 @@
CodeInspector inspector = new CodeInspector(app);
ClassSubject classSubject = inspector.clazz(TestClass.class);
assertThat(classSubject, isPresent());
- DexClass clazz = classSubject.getDexClass();
+ DexClass clazz = classSubject.getDexProgramClass();
clazz.forEachMethod(encodedMethod -> {
Code code = encodedMethod.getCode();
assertTrue(code.isDexCode());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/InstanceFieldLoadsSeparatedByInvokeCustomTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/InstanceFieldLoadsSeparatedByInvokeCustomTest.java
index 86745e5..730c399 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/InstanceFieldLoadsSeparatedByInvokeCustomTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/InstanceFieldLoadsSeparatedByInvokeCustomTest.java
@@ -44,7 +44,6 @@
testForR8(parameters.getBackend())
.addProgramClassFileData(InstanceFieldLoadsSeparatedByInvokeCustomTestClassGenerator.dump())
.addKeepAllClassesRule()
- .allowDiagnosticWarningMessages()
.setMinApi(parameters.getApiLevel())
.compile()
.assertAllWarningMessagesMatch(containsString("Unknown bootstrap method"))
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
index b5ad06f..fc87e9b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
@@ -346,7 +346,7 @@
private List<String> instanceMethods(ClassSubject clazz) {
assertNotNull(clazz);
assertThat(clazz, isPresent());
- return Streams.stream(clazz.getDexClass().methods())
+ return Streams.stream(clazz.getDexProgramClass().methods())
.filter(method -> !method.isStatic())
.map(method -> method.method.toSourceString())
.sorted()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatenationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatenationTest.java
index 69c6355..cef72dc 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatenationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringConcatenationTest.java
@@ -88,9 +88,10 @@
}
private void test(TestRunResult result, boolean isR8, boolean isReleaseMode) throws Exception {
- // TODO(b/114002137): The lack of subtyping made the escape analysis to regard
+ // TODO(b/154899065): The lack of subtyping made the escape analysis to regard
// StringBuilder#toString as an alias-introducing instruction.
// For now, debug v.s. release mode of D8 have the same result.
+ // Use library modeling to allow this optimization.
// Smaller is better in general. If the counter part is zero, that means non-string arguments
// are used, and in that case bigger is better.
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalTest.java
index d2fa3f8..8b2c2a1 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalTest.java
@@ -59,11 +59,13 @@
ClassSubject aClassSubject = inspector.clazz(A.class);
assertThat(aClassSubject, isPresent());
- assertEquals(2, aClassSubject.getDexClass().interfaces.size());
+ assertEquals(2, aClassSubject.getDexProgramClass().interfaces.size());
assertEquals(
- aClassSubject.getDexClass().interfaces.values[0], iClassSubject.getDexClass().type);
+ aClassSubject.getDexProgramClass().interfaces.values[0],
+ iClassSubject.getDexProgramClass().type);
assertEquals(
- aClassSubject.getDexClass().interfaces.values[1], jClassSubject.getDexClass().type);
+ aClassSubject.getDexProgramClass().interfaces.values[1],
+ jClassSubject.getDexProgramClass().type);
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
index 90761db..9a342e1 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
@@ -60,9 +60,10 @@
// Verify that J is not considered an unused interface, since it provides an implementation of
// m() that happens to be used.
- assertEquals(1, aClassSubject.getDexClass().interfaces.size());
+ assertEquals(1, aClassSubject.getDexProgramClass().interfaces.size());
assertEquals(
- jClassSubject.getDexClass().type, aClassSubject.getDexClass().interfaces.values[0]);
+ jClassSubject.getDexProgramClass().type,
+ aClassSubject.getDexProgramClass().interfaces.values[0]);
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
index bbc6c26..8ea5334 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -7,7 +7,7 @@
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexField;
@@ -50,12 +50,8 @@
}
@Override
- public Value insertConstNullInstruction(IRCode code, InternalOptions options) {
- throw new Unimplemented();
- }
-
- @Override
- public Value insertConstIntInstruction(IRCode code, InternalOptions options, int value) {
+ public Value insertConstNumberInstruction(
+ IRCode code, InternalOptions options, long value, TypeElement type) {
throw new Unimplemented();
}
@@ -77,7 +73,7 @@
@Override
public void replaceCurrentInstructionWithThrowNull(
- AppView<? extends AppInfoWithSubtyping> appView,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
IRCode code,
ListIterator<BasicBlock> blockIterator,
Set<BasicBlock> blocksToRemove,
diff --git a/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java b/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
index b4dc614..fd893ea 100644
--- a/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
+++ b/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
@@ -5,7 +5,6 @@
import static java.util.Collections.emptyList;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
import com.android.tools.r8.ThrowingBiFunction;
import com.android.tools.r8.ToolHelper.DexVm.Version;
@@ -17,7 +16,6 @@
import com.android.tools.r8.jasmin.JasminBuilder.ClassFileVersion;
import com.android.tools.r8.utils.ThrowingSupplier;
import java.util.function.BiConsumer;
-import java.util.function.Predicate;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -293,8 +291,7 @@
" invokespecial SubClass/<init>()V",
" invokespecial SubClass/aMethod()V",
" return");
- ensureExceptionOrCompilerError(builder, IllegalAccessError.class,
- compiler -> compiler.equals(CompilerUnderTest.R8));
+ ensureException(builder, IllegalAccessError.class);
}
@Test
@@ -613,18 +610,14 @@
(a, m) -> runOnArtR8Raw(a, m, keepMainProguardConfiguration(MAIN_CLASS), null));
}
- private void ensureExceptionOrCompilerError(JasminBuilder app,
- Class<? extends Throwable> exception,
- Predicate<CompilerUnderTest> predicate) throws Exception {
+ private void ensureException(JasminBuilder app, Class<? extends Throwable> exception)
+ throws Exception {
String name = exception.getSimpleName();
BiConsumer<ThrowingSupplier<ProcessResult, Exception>, CompilerUnderTest> runtest =
(process, compiler) -> {
try {
ProcessResult result = process.get();
- Assert.assertFalse(compiler != null && predicate.test(compiler));
Assert.assertTrue(result.stderr.contains(name));
- } catch (CompilationFailedException e) {
- Assert.assertTrue(compiler == null || predicate.test(compiler));
} catch (Exception e) {
Assert.fail();
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index 5d55ba2..d8419ca 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -66,10 +66,11 @@
}
private static Predicate<DexType> createLambdaCheck(CodeInspector inspector) {
- Set<DexType> lambdaClasses = inspector.allClasses().stream()
- .filter(clazz -> isLambda(clazz.getDexClass()))
- .map(clazz -> clazz.getDexClass().type)
- .collect(Collectors.toSet());
+ Set<DexType> lambdaClasses =
+ inspector.allClasses().stream()
+ .filter(clazz -> isLambda(clazz.getDexProgramClass()))
+ .map(clazz -> clazz.getDexProgramClass().type)
+ .collect(Collectors.toSet());
return lambdaClasses::contains;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
index 77de408..782eb68 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
@@ -10,12 +10,10 @@
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
-import java.util.function.Consumer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -28,14 +26,6 @@
return buildParameters(KotlinTargetVersion.values(), BooleanUtils.values());
}
- private Consumer<InternalOptions> optionsModifier =
- o -> {
- o.enableInlining = true;
- o.enableLambdaMerging = true;
- o.enableArgumentRemoval = true;
- o.enableUnusedArgumentRemoval = true;
- };
-
public KotlinUnusedArgumentsInLambdasTest(
KotlinTargetVersion targetVersion, boolean allowAccessModification) {
super(targetVersion, allowAccessModification);
@@ -44,35 +34,43 @@
@Test
public void testMergingKStyleLambdasAfterUnusedArgumentRemoval() throws Exception {
final String mainClassName = "unused_arg_in_lambdas_kstyle.MainKt";
- runTest("unused_arg_in_lambdas_kstyle", mainClassName, optionsModifier, app -> {
- CodeInspector inspector = new CodeInspector(app);
- inspector.forAllClasses(classSubject -> {
- if (classSubject.getOriginalDescriptor().contains("$ks")) {
- MethodSubject init = classSubject.init(ImmutableList.of("int"));
- assertThat(init, isPresent());
- // Arity 2 should appear.
- assertTrue(init.iterateInstructions(i -> i.isConstNumber(2)).hasNext());
+ runTest(
+ "unused_arg_in_lambdas_kstyle",
+ mainClassName,
+ app -> {
+ CodeInspector inspector = new CodeInspector(app);
+ inspector.forAllClasses(
+ classSubject -> {
+ if (classSubject.getOriginalDescriptor().contains("$ks")) {
+ MethodSubject init = classSubject.init(ImmutableList.of("int"));
+ assertThat(init, isPresent());
+ // Arity 2 should appear.
+ assertTrue(init.iterateInstructions(i -> i.isConstNumber(2)).hasNext());
- MethodSubject invoke = classSubject.uniqueMethodWithName("invoke");
- assertThat(invoke, isPresent());
- assertEquals(2, invoke.getMethod().method.proto.parameters.size());
- }
- });
- });
+ MethodSubject invoke = classSubject.uniqueMethodWithName("invoke");
+ assertThat(invoke, isPresent());
+ assertEquals(2, invoke.getMethod().method.proto.parameters.size());
+ }
+ });
+ });
}
@Test
public void testMergingJStyleLambdasAfterUnusedArgumentRemoval() throws Exception {
final String mainClassName = "unused_arg_in_lambdas_jstyle.MainKt";
- runTest("unused_arg_in_lambdas_jstyle", mainClassName, optionsModifier, app -> {
- CodeInspector inspector = new CodeInspector(app);
- inspector.forAllClasses(classSubject -> {
- if (classSubject.getOriginalDescriptor().contains("$js")) {
- MethodSubject get = classSubject.uniqueMethodWithName("get");
- assertThat(get, isPresent());
- assertEquals(3, get.getMethod().method.proto.parameters.size());
- }
- });
- });
+ runTest(
+ "unused_arg_in_lambdas_jstyle",
+ mainClassName,
+ app -> {
+ CodeInspector inspector = new CodeInspector(app);
+ inspector.forAllClasses(
+ classSubject -> {
+ if (classSubject.getOriginalDescriptor().contains("$js")) {
+ MethodSubject get = classSubject.uniqueMethodWithName("get");
+ assertThat(get, isPresent());
+ assertEquals(3, get.getMethod().method.proto.parameters.size());
+ }
+ });
+ });
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
index bab791c..6aac431 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
@@ -42,7 +42,6 @@
private Consumer<InternalOptions> getOptionsModifier() {
return opts -> {
opts.enableClassInlining = false;
- opts.enableUnusedArgumentRemoval = false;
// The test checks that the generated lambdas inherit from Function, which is not true if
// the unused interface removal is enabled.
opts.enableUnusedInterfaceRemoval = enableUnusedInterfaceRemoval;
@@ -183,16 +182,17 @@
}
private void initGroupsAndLambdas() {
- codeInspector.forAllClasses(clazz -> {
- DexClass dexClass = clazz.getDexClass();
- if (isLambdaOrGroup(dexClass)) {
- if (isLambdaGroupClass(dexClass)) {
- groups.add(dexClass);
- } else {
- lambdas.add(dexClass);
- }
- }
- });
+ codeInspector.forAllClasses(
+ clazz -> {
+ DexClass dexClass = clazz.getDexProgramClass();
+ if (isLambdaOrGroup(dexClass)) {
+ if (isLambdaGroupClass(dexClass)) {
+ groups.add(dexClass);
+ } else {
+ lambdas.add(dexClass);
+ }
+ }
+ });
}
void assertLambdaGroups(Group... groups) {
@@ -303,6 +303,7 @@
runTest(
"lambdas_kstyle_trivial",
mainClassName,
+ "-keepunusedarguments class * extends kotlin.jvm.internal.Lambda { invoke(int, short); }",
getOptionsModifier(),
app -> {
if (enableUnusedInterfaceRemoval) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
index 5799823..f3feb33 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
@@ -3,9 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin.metadata;
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.containsString;
+
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
import com.android.tools.r8.utils.DescriptorUtils;
+import org.hamcrest.Matcher;
abstract class KotlinMetadataTestBase extends AbstractR8KotlinTestBase {
@@ -16,6 +20,7 @@
static final String PKG = KotlinMetadataTestBase.class.getPackage().getName();
static final String PKG_PREFIX = DescriptorUtils.getBinaryNameFromJavaType(PKG);
+ static final String KT_ANY = "Lkotlin/Any;";
static final String KT_ARRAY = "Lkotlin/Array;";
static final String KT_CHAR_SEQUENCE = "Lkotlin/CharSequence;";
static final String KT_STRING = "Lkotlin/String;";
@@ -26,4 +31,8 @@
static final String KT_FUNCTION1 = "Lkotlin/Function1;";
static final String KT_COMPARABLE = "Lkotlin/Comparable;";
+
+ static Matcher<String> expectedInfoMessagesFromKotlinStdLib() {
+ return anyOf(containsString("Invalid descriptor"), containsString("No VersionRequirement"));
+ }
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
index e86770c..4e5ae0e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
@@ -9,6 +9,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestParameters;
@@ -147,11 +148,10 @@
List<ClassSubject> superTypes = kmClass.getSuperTypes();
assertTrue(superTypes.stream().noneMatch(
supertype -> supertype.getFinalDescriptor().contains("Impl")));
- // Can't build ClassSubject with Itf in classpath. Instead, check if the reference to Itf is
- // not altered via descriptors.
+ // The super types are changed and we should not keep any information about it in the metadata.
List<String> superTypeDescriptors = kmClass.getSuperTypeDescriptors();
- assertTrue(superTypeDescriptors.stream().noneMatch(supertype -> supertype.contains("Impl")));
- assertTrue(superTypeDescriptors.stream().anyMatch(supertype -> supertype.contains("Itf")));
+ assertEquals(1, superTypeDescriptors.size());
+ assertEquals(KT_ANY, superTypeDescriptors.get(0));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
index a9f1655..aaf2674 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
@@ -31,6 +31,7 @@
import java.util.List;
import java.util.Map;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -86,6 +87,7 @@
}
@Test
+ @Ignore("b/154300326")
public void testMetadataInExtensionFunction_merged() throws Exception {
Path libJar =
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
index afd2ac6..1606cbe 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
@@ -30,6 +30,7 @@
import java.util.List;
import java.util.Map;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -85,6 +86,7 @@
}
@Test
+ @Ignore("b/154300326")
public void testMetadataInExtensionProperty_merged() throws Exception {
Path libJar =
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
index fad1701..60cad3b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
@@ -29,6 +29,7 @@
import java.util.List;
import java.util.Map;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -84,6 +85,7 @@
}
@Test
+ @Ignore("b/154300326")
public void testMetadataInFunction_merged() throws Exception {
Path libJar =
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
index e58725d..3847134 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
@@ -7,11 +7,11 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static junit.framework.TestCase.assertNull;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
@@ -139,23 +139,22 @@
assertEquals(backingField.getJvmFieldSignatureAsString(), name.fieldSignature().asString());
assertNotNull(name.getterSignature());
assertEquals(getterForName.getJvmMethodSignatureAsString(), name.getterSignature().asString());
- assertNull(name.setterSignature());
+ assertEquals(name.setterSignature().asString(), "setName(Ljava/lang/String;)V");
KmPropertySubject familyName = kmClass.kmPropertyWithUniqueName("familyName");
assertThat(familyName, isPresent());
assertThat(familyName, not(isExtensionProperty()));
// No backing field for property `familyName`
assertNull(familyName.fieldSignature());
- assertNotNull(familyName.getterSignature());
- // No setter for property `familyName`
+ assertEquals(familyName.getterSignature().asString(), "getFamilyName()Ljava/lang/String;");
assertNull(familyName.setterSignature());
KmPropertySubject age = kmClass.kmPropertyWithUniqueName("age");
assertThat(age, isPresent());
assertThat(age, not(isExtensionProperty()));
- assertNotNull(age.fieldSignature());
- assertNotNull(age.getterSignature());
- assertNull(name.setterSignature());
+ assertEquals(age.fieldSignature().asString(), "a:I");
+ assertEquals(age.getterSignature().asString(), "getAge()I");
+ assertEquals(age.setterSignature().asString(), "setAge(I)V");
}
@Test
@@ -222,9 +221,31 @@
KmPropertySubject name = kmClass.kmPropertyWithUniqueName("name");
assertThat(name, isPresent());
assertThat(name, not(isExtensionProperty()));
- assertNull(name.fieldSignature());
- assertNull(name.getterSignature());
- assertNotNull(name.setterSignature());
+ // Oddly, the signature of field and getter are present even if there is only a setter:
+ // # KmProperty{
+ // # flags: 1798,
+ // # name: name,
+ // # receiverParameterType: null,
+ // # returnType: KmType{
+ // # flags: 0,
+ // # classifier: Class(name=kotlin/String),
+ // # arguments: KmTypeProjection[],
+ // # abbreviatedType: null,
+ // # outerType: null,
+ // # raw: false,
+ // # annotations: KmAnnotion[],
+ // # },
+ // # typeParameters: KmTypeParameter[],
+ // # getterFlags: 6,
+ // # setterFlags: 6,
+ // # setterParameter: null,
+ // # jvmFlags: 0,
+ // # fieldSignature: name:Ljava/lang/String;,
+ // # getterSignature: getName()Ljava/lang/String;,
+ // # setterSignature: setName(Ljava/lang/String;)V,
+ assertEquals(name.fieldSignature().asString(), "name:Ljava/lang/String;");
+ assertEquals(name.getterSignature().asString(), "getName()Ljava/lang/String;");
+ assertEquals(name.setterSignature().asString(), "setName(Ljava/lang/String;)V");
KmPropertySubject familyName = kmClass.kmPropertyWithUniqueName("familyName");
assertThat(familyName, not(isPresent()));
@@ -232,8 +253,8 @@
KmPropertySubject age = kmClass.kmPropertyWithUniqueName("age");
assertThat(age, isPresent());
assertThat(age, not(isExtensionProperty()));
- assertNotNull(age.fieldSignature());
- assertNull(age.getterSignature());
- assertNotNull(age.setterSignature());
+ assertEquals(age.fieldSignature().asString(), "a:I");
+ assertEquals(age.getterSignature().asString(), "getAge()I");
+ assertEquals(age.setterSignature().asString(), "setAge(I)V");
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
index 6bfafc4..f474cf1 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
@@ -105,8 +105,10 @@
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.allowDiagnosticWarningMessages()
.compile()
- .assertWarningMessageThatMatches(
- equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .assertWarningMessageThatMatches(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+ // TODO(b/155536535): Enable this assert.
+ // .assertInfoMessageThatMatches(expectedInfoMessagesFromKotlinStdLib())
+ .assertNoErrorMessages()
.inspect(this::inspect);
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
index b1af80f..26ea8de 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
@@ -185,8 +185,8 @@
// Check that typealias API = Itf has been rewritten correctly.
KmTypeAliasSubject api = kmPackage.kmTypeAliasWithUniqueName("API");
assertThat(api, isPresent());
- assertThat(api.expandedType(), isDexClass(itf.getDexClass()));
- assertThat(api.underlyingType(), isDexClass(itf.getDexClass()));
+ assertThat(api.expandedType(), isDexClass(itf.getDexProgramClass()));
+ assertThat(api.underlyingType(), isDexClass(itf.getDexProgramClass()));
// Check that the type-alias APIs exist and that the expanded type is renamed.
KmTypeAliasSubject apIs = kmPackage.kmTypeAliasWithUniqueName("APIs");
@@ -194,7 +194,7 @@
assertEquals(arrayDescriptor, apIs.expandedType().descriptor());
assertEquals(1, apIs.expandedType().typeArguments().size());
KmTypeProjectionSubject expandedArgument = apIs.expandedType().typeArguments().get(0);
- assertThat(expandedArgument.type(), isDexClass(itf.getDexClass()));
+ assertThat(expandedArgument.type(), isDexClass(itf.getDexProgramClass()));
assertEquals(myAliasedArray.descriptor(packageName), apIs.underlyingType().descriptor());
assertEquals(1, apIs.underlyingType().typeArguments().size());
@@ -219,15 +219,15 @@
// typealias Arr1D<K> = Arr<K>
KmTypeAliasSubject arr1D = kmPackage.kmTypeAliasWithUniqueName("Arr1D");
assertThat(arr1D, isPresent());
- assertThat(arr1D.expandedType(), isDexClass(arr.getDexClass()));
+ assertThat(arr1D.expandedType(), isDexClass(arr.getDexProgramClass()));
// typealias Arr2D<K> = Arr1D<Arr1D<K>>
KmTypeAliasSubject arr2D = kmPackage.kmTypeAliasWithUniqueName("Arr2D");
assertThat(arr2D, isPresent());
- assertThat(arr2D.expandedType(), isDexClass(arr.getDexClass()));
+ assertThat(arr2D.expandedType(), isDexClass(arr.getDexProgramClass()));
assertEquals(1, arr2D.expandedType().typeArguments().size());
KmTypeProjectionSubject arr2DexpandedArg = arr2D.expandedType().typeArguments().get(0);
- assertThat(arr2DexpandedArg.type(), isDexClass(arr.getDexClass()));
+ assertThat(arr2DexpandedArg.type(), isDexClass(arr.getDexProgramClass()));
assertEquals(arr1D.descriptor(packageName), arr2D.underlyingType().descriptor());
assertEquals(1, arr2D.underlyingType().typeArguments().size());
@@ -256,7 +256,7 @@
assertEquals(intSet.descriptor(packageName), underlyingType.descriptor());
KmTypeSubject expandedType = myMapToSetOfInt.expandedType().typeArguments().get(1).type();
- assertEquals(intSet.expandedType(), expandedType);
+ assertTrue(intSet.expandedType().equalUpToAbbreviatedType(expandedType));
// Check that the following exist:
// typealias MyHandler = (Int, Any) -> Unit
@@ -282,6 +282,6 @@
assertThat(classWithCompanionC, isPresent());
ClassSubject companionClazz = inspector.clazz(packageName + ".ClassWithCompanion$Companion");
- assertThat(classWithCompanionC.expandedType(), isDexClass(companionClazz.getDexClass()));
+ assertThat(classWithCompanionC.expandedType(), isDexClass(companionClazz.getDexProgramClass()));
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
index a158b99..2e3dd21 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
@@ -213,12 +213,13 @@
KmTypeParameterSubject methodTypeParameter = funGenericsWithUpperBounds.typeParameters().get(0);
List<KmTypeSubject> upperBounds = methodTypeParameter.upperBounds();
assertEquals(2, upperBounds.size());
- assertThat(upperBounds.get(0), isDexClass(someClass.getDexClass()));
+ assertThat(upperBounds.get(0), isDexClass(someClass.getDexProgramClass()));
assertEquals(KT_COMPARABLE, upperBounds.get(1).descriptor());
// Check that the upper bound has a type argument.
assertEquals(1, upperBounds.get(1).typeArguments().size());
assertThat(
- upperBounds.get(1).typeArguments().get(0).type(), isDexClass(someClass.getDexClass()));
+ upperBounds.get(1).typeArguments().get(0).type(),
+ isDexClass(someClass.getDexProgramClass()));
}
private void inspectCoVariant(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
index c120f9d..fcb5958 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.kotlin.metadata;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNotNull;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertNull;
@@ -19,6 +20,7 @@
import java.io.IOException;
import java.util.Collection;
import java.util.concurrent.ExecutionException;
+import kotlinx.metadata.jvm.KotlinClassHeader;
import kotlinx.metadata.jvm.KotlinClassMetadata;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -50,6 +52,9 @@
.addKeepRules("-keep class kotlin.Metadata")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.compile()
+ // TODO(b/155536535): Enable this assert.
+ // .assertAllInfoMessagesMatch(expectedInfoMessagesFromKotlinStdLib())
+ // .assertInfosCount(5)
.inspect(this::inspect);
}
@@ -59,12 +64,26 @@
ClassSubject r8Clazz = inspector.clazz(clazzSubject.getOriginalName());
assertThat(r8Clazz, isPresent());
KotlinClassMetadata originalMetadata = clazzSubject.getKotlinClassMetadata();
+ KotlinClassMetadata rewrittenMetadata = r8Clazz.getKotlinClassMetadata();
if (originalMetadata == null) {
- assertNull(r8Clazz.getKotlinClassMetadata());
+ assertNull(rewrittenMetadata);
continue;
}
- assertNotNull(r8Clazz.getKotlinClassMetadata());
- // TODO(b/152153136): Extend the test with assertions about metadata equality.
+ assertNotNull(rewrittenMetadata);
+ KotlinClassHeader originalHeader = originalMetadata.getHeader();
+ KotlinClassHeader rewrittenHeader = rewrittenMetadata.getHeader();
+ assertEquals(originalHeader.getKind(), rewrittenHeader.getKind());
+ // TODO(b/154199572): Should we check for meta-data version?
+ assertEquals(originalHeader.getPackageName(), rewrittenHeader.getPackageName());
+ // We cannot assert equality of the data since it may be ordered differently. Instead we use
+ // the KotlinMetadataWriter.
+ // TODO(b/155571455): Deactivating the method call to kotlinMetadataString until resolved.
+ // String expected = KotlinMetadataWriter.kotlinMetadataToString("", originalMetadata);
+ // String actual = KotlinMetadataWriter.kotlinMetadataToString("", rewrittenMetadata);
+ // // TODO(b/155534905): For invalid synthetic class lambdas, we emit null after rewriting.
+ // if (clazzSubject.getKotlinClassMetadata().getHeader().getKind() != 3) {
+ // assertEquals(expected, actual);
+ // }
}
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
index 4d66342..94edd94 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -9,7 +9,6 @@
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
-import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
@@ -24,7 +23,7 @@
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
-public class MetadataStripTest extends KotlinTestBase {
+public class MetadataStripTest extends KotlinMetadataTestBase {
private final TestParameters parameters;
@@ -54,11 +53,13 @@
.addKeepRules("-keep class kotlin.Metadata")
// TODO(b/151194540): if this option is settled down, this test is meaningless.
.addOptionsModification(o -> o.enableKotlinMetadataRewritingForRenamedClasses = false)
- .allowDiagnosticWarningMessages()
+ .allowDiagnosticMessages()
.setMinApi(parameters.getApiLevel())
.compile()
.assertAllWarningMessagesMatch(
equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .assertAllInfoMessagesMatch(expectedInfoMessagesFromKotlinStdLib())
+ .assertNoErrorMessages()
.run(parameters.getRuntime(), mainClassName);
CodeInspector inspector = result.inspector();
ClassSubject clazz = inspector.clazz(mainClassName);
diff --git a/src/test/java/com/android/tools/r8/naming/ClassObfuscationDictionaryDuplicateTest.java b/src/test/java/com/android/tools/r8/naming/ClassObfuscationDictionaryDuplicateTest.java
index 4f3a868..e0ef67d 100644
--- a/src/test/java/com/android/tools/r8/naming/ClassObfuscationDictionaryDuplicateTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ClassObfuscationDictionaryDuplicateTest.java
@@ -40,7 +40,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public ClassObfuscationDictionaryDuplicateTest(TestParameters parameters) {
@@ -61,8 +61,7 @@
.noTreeShaking()
.addKeepRules("-classobfuscationdictionary " + dictionary.toString())
.addKeepMainRule(C.class)
- .setMinApi(parameters.getRuntime())
- .compile()
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), C.class)
.assertSuccessWithOutput("HELLO WORLD!")
.inspect(
diff --git a/src/test/java/com/android/tools/r8/naming/DontUseMixedCaseClassNamesExistingClassTest.java b/src/test/java/com/android/tools/r8/naming/DontUseMixedCaseClassNamesExistingClassTest.java
new file mode 100644
index 0000000..b16347d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/DontUseMixedCaseClassNamesExistingClassTest.java
@@ -0,0 +1,82 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.FileUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+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 DontUseMixedCaseClassNamesExistingClassTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final boolean dontUseMixedCase;
+ private static final String EXPECTED = "A.foo";
+ private static final String FINAL_CLASS_NAME = "DontUseMixedCaseClassNamesExistingClassTest$main";
+
+ @Parameters(name = "{0}, dontusemixedcaseclassnames: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+ }
+
+ public DontUseMixedCaseClassNamesExistingClassTest(
+ TestParameters parameters, boolean dontUseMixedCase) {
+ this.parameters = parameters;
+ this.dontUseMixedCase = dontUseMixedCase;
+ }
+
+ @Test
+ public void testR8() throws ExecutionException, CompilationFailedException, IOException {
+ Path dictionary = temp.getRoot().toPath().resolve("dictionary.txt");
+ FileUtils.writeTextFile(dictionary, FINAL_CLASS_NAME);
+ testForR8(parameters.getBackend())
+ .addProgramClasses(A.class, Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepClassRulesWithAllowObfuscation(A.class)
+ .addKeepMainRule(Main.class)
+ .addKeepRules("-classobfuscationdictionary " + dictionary.toString())
+ .ifTrue(dontUseMixedCase, b -> b.addKeepRules("-dontusemixedcaseclassnames"))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED)
+ .inspect(
+ inspector -> {
+ String finalName = Main.class.getPackage().getName() + "." + FINAL_CLASS_NAME;
+ assertEquals(finalName.toLowerCase(), Main.class.getTypeName().toLowerCase());
+ if (dontUseMixedCase) {
+ assertNotEquals(finalName, inspector.clazz(A.class).getFinalName());
+ } else {
+ assertEquals(finalName, inspector.clazz(A.class).getFinalName());
+ }
+ });
+ }
+
+ public static class A { // Will be renamed to main if not -dontusemixedcaseclassnames
+
+ public void foo() {
+ System.out.println("A.foo");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new A().foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
index 627a059..ad8c526 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
@@ -148,7 +148,7 @@
assertEquals(countInABar, renamedYetFoundIdentifierCount);
renamedYetFoundIdentifierCount =
- countRenamedClassIdentifier(inspector, aClass.getDexClass().staticFields());
+ countRenamedClassIdentifier(inspector, aClass.getDexProgramClass().staticFields());
assertEquals(countInAFields, renamedYetFoundIdentifierCount);
}
@@ -208,7 +208,7 @@
assertEquals(0, renamedYetFoundIdentifierCount);
renamedYetFoundIdentifierCount =
- countRenamedClassIdentifier(inspector, aClass.getDexClass().staticFields());
+ countRenamedClassIdentifier(inspector, aClass.getDexProgramClass().staticFields());
assertEquals(0, renamedYetFoundIdentifierCount);
}
@@ -229,7 +229,7 @@
assertEquals(1, renamedYetFoundIdentifierCount);
renamedYetFoundIdentifierCount =
- countRenamedClassIdentifier(inspector, aClass.getDexClass().staticFields());
+ countRenamedClassIdentifier(inspector, aClass.getDexProgramClass().staticFields());
assertEquals(2, renamedYetFoundIdentifierCount);
}
diff --git a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
index f0b3c24..385911c 100644
--- a/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/NamingTestBase.java
@@ -5,11 +5,12 @@
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.EnqueuerFactory;
import com.android.tools.r8.shaking.ProguardConfiguration;
@@ -61,18 +62,20 @@
protected NamingLens runMinifier(List<Path> configPaths) throws ExecutionException {
ProguardConfiguration configuration =
ToolHelper.loadProguardConfiguration(dexItemFactory, configPaths);
+
InternalOptions options = new InternalOptions(configuration, new Reporter());
options.programConsumer = DexIndexedConsumer.emptyConsumer();
ExecutorService executor = ThreadUtils.getExecutorService(1);
- AppView<AppInfoWithSubtyping> appView =
- AppView.createForR8(new AppInfoWithSubtyping(program), options);
+ AppView<AppInfoWithClassHierarchy> appView =
+ AppView.createForR8(new AppInfoWithClassHierarchy(program), options);
+ SubtypingInfo subtypingInfo = new SubtypingInfo(program.allClasses(), program);
appView.setRootSet(
- new RootSetBuilder(appView, program, configuration.getRules()).run(executor));
+ new RootSetBuilder(appView, subtypingInfo, configuration.getRules()).run(executor));
appView.setAppServices(AppServices.builder(appView).build());
- Enqueuer enqueuer = EnqueuerFactory.createForInitialTreeShaking(appView);
+ Enqueuer enqueuer = EnqueuerFactory.createForInitialTreeShaking(appView, subtypingInfo);
appView.setAppInfo(
enqueuer.traceApplication(
appView.rootSet(), configuration.getDontWarnPatterns(), executor, timing));
diff --git a/src/test/java/com/android/tools/r8/naming/NonMemberClassTest.java b/src/test/java/com/android/tools/r8/naming/NonMemberClassTest.java
index 04c39b4..f777cda 100644
--- a/src/test/java/com/android/tools/r8/naming/NonMemberClassTest.java
+++ b/src/test/java/com/android/tools/r8/naming/NonMemberClassTest.java
@@ -117,9 +117,12 @@
}
assertEquals(
expectedNumberOfNonMemberInnerClasses,
- inspector.allClasses().stream().filter(classSubject ->
- classSubject.getDexClass().isLocalClass()
- || classSubject.getDexClass().isAnonymousClass()).count());
+ inspector.allClasses().stream()
+ .filter(
+ classSubject ->
+ classSubject.getDexProgramClass().isLocalClass()
+ || classSubject.getDexProgramClass().isAnonymousClass())
+ .count());
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/PackageObfuscationDictionaryDuplicateTest.java b/src/test/java/com/android/tools/r8/naming/PackageObfuscationDictionaryDuplicateTest.java
index f10947e..133e607 100644
--- a/src/test/java/com/android/tools/r8/naming/PackageObfuscationDictionaryDuplicateTest.java
+++ b/src/test/java/com/android/tools/r8/naming/PackageObfuscationDictionaryDuplicateTest.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.naming.keeppackagenames.Top;
-import com.android.tools.r8.naming.packageobfucationdict.A;
+import com.android.tools.r8.naming.packageobfuscationdict.A;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import java.io.IOException;
diff --git a/src/test/java/com/android/tools/r8/naming/RenameSourceFileRetraceTest.java b/src/test/java/com/android/tools/r8/naming/RenameSourceFileRetraceTest.java
index 4de8e4b..6f5aade 100644
--- a/src/test/java/com/android/tools/r8/naming/RenameSourceFileRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/RenameSourceFileRetraceTest.java
@@ -125,7 +125,7 @@
private void inspectSourceFileForClass(CodeInspector inspector, Class<?> clazz, String expected) {
ClassSubject classToBeMinifiedSubject = inspector.clazz(clazz);
assertThat(classToBeMinifiedSubject, isPresent());
- DexClass dexClass = classToBeMinifiedSubject.getDexClass();
+ DexClass dexClass = classToBeMinifiedSubject.getDexProgramClass();
String actualString = dexClass.sourceFile == null ? null : dexClass.sourceFile.toString();
assertEquals(expected, actualString);
}
diff --git a/src/test/java/com/android/tools/r8/naming/b155249069/A/A.java b/src/test/java/com/android/tools/r8/naming/b155249069/A/A.java
new file mode 100644
index 0000000..c0690b6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/b155249069/A/A.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming.b155249069.A;
+
+public class A {
+
+ public void foo() {
+ System.out.println("A.A.foo()");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/b155249069/DontUseMixedCaseClassNamesExistingClassPackageTest.java b/src/test/java/com/android/tools/r8/naming/b155249069/DontUseMixedCaseClassNamesExistingClassPackageTest.java
new file mode 100644
index 0000000..a55ef4b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/b155249069/DontUseMixedCaseClassNamesExistingClassPackageTest.java
@@ -0,0 +1,82 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming.b155249069;
+
+import static junit.framework.TestCase.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.naming.b155249069.package_b.A;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+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 DontUseMixedCaseClassNamesExistingClassPackageTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final boolean dontUseMixedCase;
+
+ @Parameters(name = "{0}, dontusemixedcaseclassnames: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+ }
+
+ public DontUseMixedCaseClassNamesExistingClassPackageTest(
+ TestParameters parameters, boolean dontUseMixedCase) {
+ this.parameters = parameters;
+ this.dontUseMixedCase = dontUseMixedCase;
+ }
+
+ @Test
+ public void testR8() throws ExecutionException, CompilationFailedException, IOException {
+ Path packageDictionary = temp.getRoot().toPath().resolve("packagedictionary.txt");
+ // Suggest the name 'a' for the package, to force a collision with A.A.
+ FileUtils.writeTextFile(packageDictionary, "a");
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, com.android.tools.r8.naming.b155249069.A.A.class, A.class)
+ .setMinApi(parameters.getApiLevel())
+ // Keep A.A such that the package A is kept.
+ .addKeepClassRules(com.android.tools.r8.naming.b155249069.A.A.class)
+ .addKeepClassRulesWithAllowObfuscation(A.class)
+ .addKeepMainRule(Main.class)
+ .addKeepRules("-packageobfuscationdictionary " + packageDictionary.toString())
+ .ifTrue(dontUseMixedCase, b -> b.addKeepRules("-dontusemixedcaseclassnames"))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A.A.foo()", "package_b.B.foo()")
+ .inspect(
+ inspector -> {
+ ClassSubject aSubject =
+ inspector.clazz(com.android.tools.r8.naming.b155249069.A.A.class);
+ ClassSubject bSubject = inspector.clazz(A.class);
+ if (dontUseMixedCase) {
+ assertNotEquals(
+ aSubject.getFinalName().toLowerCase(), bSubject.getFinalName().toLowerCase());
+ } else {
+ assertEquals(
+ aSubject.getFinalName().toLowerCase(), bSubject.getFinalName().toLowerCase());
+ }
+ });
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new com.android.tools.r8.naming.b155249069.A.A().foo();
+ new A().foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/b155249069/package_b/A.java b/src/test/java/com/android/tools/r8/naming/b155249069/package_b/A.java
new file mode 100644
index 0000000..a02a90d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/b155249069/package_b/A.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming.b155249069.package_b;
+
+public class A {
+
+ public void foo() {
+ System.out.println("package_b.B.foo()");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/packageobfucationdict/A.java b/src/test/java/com/android/tools/r8/naming/packageobfuscationdict/A.java
similarity index 79%
rename from src/test/java/com/android/tools/r8/naming/packageobfucationdict/A.java
rename to src/test/java/com/android/tools/r8/naming/packageobfuscationdict/A.java
index 012140e..67f256b 100644
--- a/src/test/java/com/android/tools/r8/naming/packageobfucationdict/A.java
+++ b/src/test/java/com/android/tools/r8/naming/packageobfuscationdict/A.java
@@ -2,6 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.naming.packageobfucationdict;
+package com.android.tools.r8.naming.packageobfuscationdict;
public class A {}
diff --git a/src/test/java/com/android/tools/r8/regress/B76025099.java b/src/test/java/com/android/tools/r8/regress/B76025099.java
index 5c5f494..59cfbe8 100644
--- a/src/test/java/com/android/tools/r8/regress/B76025099.java
+++ b/src/test/java/com/android/tools/r8/regress/B76025099.java
@@ -143,7 +143,7 @@
FieldAccessInstructionSubject fieldAccessInstruction =
(FieldAccessInstructionSubject) instruction;
assertEquals("name", fieldAccessInstruction.name());
- assertTrue(fieldAccessInstruction.holder().is(impl.getDexClass().type.toString()));
+ assertTrue(fieldAccessInstruction.holder().is(impl.getDexProgramClass().type.toString()));
assertNotNull(findInstructionOrNull(iterator, InstructionSubject::isReturnVoid));
diff --git a/src/test/java/com/android/tools/r8/regress/Regress37740372.java b/src/test/java/com/android/tools/r8/regress/Regress37740372.java
index 34e4f3e..8047716 100644
--- a/src/test/java/com/android/tools/r8/regress/Regress37740372.java
+++ b/src/test/java/com/android/tools/r8/regress/Regress37740372.java
@@ -126,7 +126,7 @@
private void assertIsJavaLangObjet(ClassSubject clazz) {
assertTrue(clazz.getOriginalDescriptor().equals("Ljava/lang/Object;"));
- assertNull(clazz.getDexClass().superType);
+ assertNull(clazz.getDexProgramClass().superType);
}
private void checkApplicationOnlyHasJavaLangObject(AndroidApp app) throws Throwable {
diff --git a/src/test/java/com/android/tools/r8/regress/b152800551/FailedStaticizingRegressionTest.java b/src/test/java/com/android/tools/r8/regress/b152800551/StaticizerGetterRewritingRegressionTest.java
similarity index 93%
rename from src/test/java/com/android/tools/r8/regress/b152800551/FailedStaticizingRegressionTest.java
rename to src/test/java/com/android/tools/r8/regress/b152800551/StaticizerGetterRewritingRegressionTest.java
index 7ec8ab0..5162092 100644
--- a/src/test/java/com/android/tools/r8/regress/b152800551/FailedStaticizingRegressionTest.java
+++ b/src/test/java/com/android/tools/r8/regress/b152800551/StaticizerGetterRewritingRegressionTest.java
@@ -14,7 +14,7 @@
// This is a reproduction of b/152800551.
@RunWith(Parameterized.class)
-public class FailedStaticizingRegressionTest extends TestBase {
+public class StaticizerGetterRewritingRegressionTest extends TestBase {
private static final String EXPECTED =
StringUtils.lines(
@@ -27,7 +27,7 @@
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- public FailedStaticizingRegressionTest(TestParameters parameters) {
+ public StaticizerGetterRewritingRegressionTest(TestParameters parameters) {
this.parameters = parameters;
}
diff --git a/src/test/java/com/android/tools/r8/relocator/RelocatorCommandTest.java b/src/test/java/com/android/tools/r8/relocator/RelocatorCommandTest.java
new file mode 100644
index 0000000..56e1f5d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/relocator/RelocatorCommandTest.java
@@ -0,0 +1,193 @@
+// 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.relocator;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.ProgramResourceProvider;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.PackageReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.ArchiveResourceProvider;
+import java.io.IOException;
+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.Parameters;
+
+@RunWith(Parameterized.class)
+public class RelocatorCommandTest extends TestBase {
+
+ private static final PackageReference SOURCE = Reference.packageFromString("foo");
+ private static final PackageReference DESTINATION = Reference.packageFromString("bar");
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public RelocatorCommandTest(TestParameters parameters) {}
+
+ @Test
+ public void testCommandBuilder() throws CompilationFailedException, IOException {
+ Path input1 = temp.newFile("in1.jar").toPath();
+ Path input2 = temp.newFile("in2.jar").toPath();
+ Path output = temp.newFile("output.jar").toPath();
+ RelocatorCommand command =
+ RelocatorCommand.builder()
+ .setThreadCount(42)
+ .setOutputPath(output)
+ .addProgramFiles(input1, input2)
+ .addPackageMapping(SOURCE, DESTINATION)
+ .build();
+ assertEquals(42, command.getThreadCount());
+ assertNotNull(command.getMapping());
+ assertEquals(DESTINATION, command.getMapping().get(SOURCE));
+ List<ProgramResourceProvider> programResources = command.getApp().getProgramResourceProviders();
+ assertEquals(2, programResources.size());
+ for (ProgramResourceProvider programResourceProvider : programResources) {
+ assertTrue(programResourceProvider instanceof ArchiveResourceProvider);
+ }
+ }
+
+ @Test
+ public void testPrint() throws CompilationFailedException {
+ RelocatorCommand.Builder builder =
+ RelocatorCommand.builder().setPrintHelp(true).setPrintVersion(true);
+ RelocatorCommand command = builder.build();
+ assertTrue(command.isPrintHelp());
+ assertTrue(command.isPrintVersion());
+ assertNull(command.getApp());
+ assertNull(command.getMapping());
+ }
+
+ @Test
+ public void testParser() {}
+
+ @Test
+ public void testInvalidThreadCount() {
+ CompilationFailedException exception =
+ assertThrows(
+ CompilationFailedException.class,
+ () -> {
+ Path input1 = temp.newFile("in1.jar").toPath();
+ Path input2 = temp.newFile("in2.jar").toPath();
+ Path output = temp.newFile("output.jar").toPath();
+ RelocatorCommand.builder()
+ .setThreadCount(-2)
+ .setOutputPath(output)
+ .addProgramFiles(input1, input2)
+ .addPackageMapping(SOURCE, DESTINATION)
+ .build();
+ });
+ assertThat(exception.getCause().getMessage(), containsString("Invalid threadCount: -2"));
+ }
+
+ @Test
+ public void testUnknownArgument() {
+ CompilationFailedException exception =
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ RelocatorCommand.parse(new String[] {"--unknown-argument"}, Origin.unknown())
+ .build());
+ assertThat(
+ exception.getCause().getMessage(), containsString("Unknown argument: --unknown-argument"));
+ }
+
+ @Test
+ public void testDuplicateOutputPaths() {
+ CompilationFailedException exception =
+ assertThrows(
+ CompilationFailedException.class,
+ () -> {
+ Path input1 = temp.newFile("in.jar").toPath();
+ RelocatorCommand.parse(
+ new String[] {
+ "--output",
+ "first_output",
+ "--output",
+ "another_output",
+ "--map",
+ "foo->bar",
+ "--input",
+ input1.toString()
+ },
+ Origin.unknown())
+ .build();
+ });
+ assertThat(
+ exception.getCause().getMessage(),
+ containsString("Cannot output both to 'first_output' and 'another_output'"));
+ }
+
+ @Test
+ public void testNoOutput() {
+ CompilationFailedException exception =
+ assertThrows(
+ CompilationFailedException.class,
+ () -> {
+ Path input1 = temp.newFile("in.jar").toPath();
+ RelocatorCommand.builder()
+ .addProgramFile(input1)
+ .addPackageMapping(SOURCE, DESTINATION)
+ .build();
+ });
+ assertThat(
+ exception.getCause().getMessage(),
+ containsString("No output path or consumer has been specified"));
+ }
+
+ @Test
+ public void testConsumer() throws IOException, CompilationFailedException {
+ Path input = temp.newFile("input.jar").toPath();
+ Path output = temp.newFile("output.jar").toPath();
+ ArchiveConsumer programConsumer = new ArchiveConsumer(output);
+ RelocatorCommand command =
+ RelocatorCommand.builder()
+ .setThreadCount(42)
+ .addProgramFiles(input)
+ .setConsumer(programConsumer)
+ .addPackageMapping(SOURCE, DESTINATION)
+ .build();
+ assertEquals(command.getConsumer(), programConsumer);
+ }
+
+ @Test
+ public void testInvalidPackage() {
+ IllegalArgumentException exception =
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> {
+ Path input1 = temp.newFile("in1.jar").toPath();
+ RelocatorCommand.parse(
+ new String[] {
+ "--output",
+ "output",
+ "--map",
+ "invalid;package-name->bar",
+ "--input",
+ input1.toString()
+ },
+ Origin.unknown())
+ .build();
+ });
+ assertThat(
+ exception.getMessage(), containsString("Package name 'invalid;package-name' is not valid"));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/relocator/RelocatorNoneClassFileTest.java b/src/test/java/com/android/tools/r8/relocator/RelocatorNoneClassFileTest.java
new file mode 100644
index 0000000..deccd90
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/relocator/RelocatorNoneClassFileTest.java
@@ -0,0 +1,83 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.relocator;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertNull;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.ResourceException;
+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.ZipUtils;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.Scanner;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+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 RelocatorNoneClassFileTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public RelocatorNoneClassFileTest(TestParameters parameters) {}
+
+ @Test
+ public void testRewritingFiles()
+ throws IOException, CompilationFailedException, ResourceException {
+ File testJar = temp.newFile("test.jar");
+ Path testJarPath = testJar.toPath();
+ OpenOption[] options =
+ new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
+ try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(testJarPath, options))) {
+ ZipUtils.writeToZipStream(
+ out, "foo/bar/kotlin.kotlin_builtins", "foo.bar.BazImpl".getBytes(), ZipEntry.STORED);
+ ZipUtils.writeToZipStream(
+ out, "foo.bar.kotlin.kotlin_builtins", "foo.bar.BazImpl".getBytes(), ZipEntry.STORED);
+ ZipUtils.writeToZipStream(
+ out,
+ "somepackage/foo/bar/kotlin.kotlin_builtins",
+ "foo.bar.BazImpl".getBytes(),
+ ZipEntry.STORED);
+ }
+ Path relocatedJar = temp.newFile("out.jar").toPath();
+ Relocator.run(
+ RelocatorCommand.builder()
+ .addProgramFile(testJarPath)
+ .setOutputPath(relocatedJar)
+ .addPackageMapping(
+ Reference.packageFromString("foo.bar"), Reference.packageFromString("baz.qux"))
+ .build());
+ ZipFile zip = new ZipFile(relocatedJar.toFile());
+ // We should have relocated foo/bar/kotlin.kotlin_builtins to baz/qux/kotlin.kotlin_builtins
+ assertNull(zip.getEntry("foo/bar/kotlin.kotlin_builtins"));
+ ZipEntry relocatedEntry = zip.getEntry("baz/qux/kotlin.kotlin_builtins");
+ assertNotNull(relocatedEntry);
+ // We should not change the contents of the files, even if it matches the package.
+ InputStream inputStream = zip.getInputStream(relocatedEntry);
+ Scanner scanner = new Scanner(inputStream);
+ assertEquals("foo.bar.BazImpl", scanner.next());
+ // We should (for now at least) not rewrite files if not in a package.
+ assertNotNull(zip.getEntry("foo.bar.kotlin.kotlin_builtins"));
+ assertNotNull(zip.getEntry("somepackage/foo/bar/kotlin.kotlin_builtins"));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/relocator/RelocatorServiceLoaderTest.java b/src/test/java/com/android/tools/r8/relocator/RelocatorServiceLoaderTest.java
new file mode 100644
index 0000000..96d429c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/relocator/RelocatorServiceLoaderTest.java
@@ -0,0 +1,152 @@
+// 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.relocator;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNotNull;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.ResourceException;
+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.relocator.foo.bar.Baz;
+import com.android.tools.r8.relocator.foo.bar.BazImpl;
+import com.android.tools.r8.relocator.foo.baz.OtherImpl;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ZipUtils;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.Scanner;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+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 RelocatorServiceLoaderTest extends TestBase {
+
+ private static final String SERVICE_FILE = "META-INF/services/foo.bar.Baz";
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public RelocatorServiceLoaderTest(TestParameters parameters) {}
+
+ @Test
+ public void testNotRewritingServiceForNotFoundClass()
+ throws IOException, CompilationFailedException, ResourceException {
+ File testJar = temp.newFile("test.jar");
+ Path testJarPath = testJar.toPath();
+ OpenOption[] options =
+ new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
+ try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(testJarPath, options))) {
+ ZipUtils.writeToZipStream(
+ out,
+ SERVICE_FILE,
+ StringUtils.lines("foo.bar.BazImpl", "foo.baz.OtherImpl").getBytes(),
+ ZipEntry.STORED);
+ }
+ ZipFile zip = new ZipFile(testJar);
+ assertNotNull(zip.getEntry(SERVICE_FILE));
+ Path relocatedJar = temp.newFile("out.jar").toPath();
+ Relocator.run(
+ RelocatorCommand.builder()
+ .addProgramFile(testJarPath)
+ .setOutputPath(relocatedJar)
+ .addPackageMapping(
+ Reference.packageFromString("foo.bar"), Reference.packageFromString("baz.qux"))
+ .build());
+ zip = new ZipFile(relocatedJar.toFile());
+ ZipEntry serviceEntry = zip.getEntry(SERVICE_FILE);
+ assertNotNull(serviceEntry);
+ }
+
+ @Test
+ public void testRewritingService()
+ throws IOException, CompilationFailedException, ResourceException {
+ File testJar = temp.newFile("test.jar");
+ Path testJarPath = testJar.toPath();
+ OpenOption[] options =
+ new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
+ try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(testJarPath, options))) {
+ ZipUtils.writeToZipStream(
+ out,
+ SERVICE_FILE,
+ StringUtils.lines("foo.bar.BazImpl", "foo.baz.OtherImpl").getBytes(),
+ ZipEntry.STORED);
+ ZipUtils.writeToZipStream(out, "foo/bar/Baz.class", Baz.dump(), ZipEntry.STORED);
+ ZipUtils.writeToZipStream(out, "foo/bar/BazImpl.class", BazImpl.dump(), ZipEntry.STORED);
+ ZipUtils.writeToZipStream(out, "foo/baz/OtherImpl.class", OtherImpl.dump(), ZipEntry.STORED);
+ }
+ ZipFile zip = new ZipFile(testJar);
+ assertNotNull(zip.getEntry(SERVICE_FILE));
+ Path relocatedJar = temp.newFile("out.jar").toPath();
+ Relocator.run(
+ RelocatorCommand.builder()
+ .addProgramFile(testJarPath)
+ .setOutputPath(relocatedJar)
+ .addPackageMapping(
+ Reference.packageFromString("foo.bar"), Reference.packageFromString("baz.qux"))
+ .build());
+ zip = new ZipFile(relocatedJar.toFile());
+ ZipEntry serviceEntry = zip.getEntry("META-INF/services/baz.qux.Baz");
+ assertNotNull(serviceEntry);
+ InputStream inputStream = zip.getInputStream(serviceEntry);
+ Scanner scanner = new Scanner(inputStream);
+ assertEquals("baz.qux.BazImpl", scanner.next());
+ assertEquals("foo.baz.OtherImpl", scanner.next());
+ assertFalse(scanner.hasNext());
+ }
+
+ @Test
+ public void testRewritingServiceImpl()
+ throws IOException, CompilationFailedException, ResourceException {
+ File testJar = temp.newFile("test.jar");
+ Path testJarPath = testJar.toPath();
+ OpenOption[] options =
+ new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
+ try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(testJarPath, options))) {
+ ZipUtils.writeToZipStream(
+ out,
+ SERVICE_FILE,
+ StringUtils.lines("foo.bar.BazImpl", "foo.baz.OtherImpl").getBytes(),
+ ZipEntry.STORED);
+ ZipUtils.writeToZipStream(out, "foo/bar/Baz.class", Baz.dump(), ZipEntry.STORED);
+ ZipUtils.writeToZipStream(out, "foo/bar/BazImpl.class", BazImpl.dump(), ZipEntry.STORED);
+ ZipUtils.writeToZipStream(out, "foo/baz/OtherImpl.class", OtherImpl.dump(), ZipEntry.STORED);
+ }
+ ZipFile zip = new ZipFile(testJar);
+ assertNotNull(zip.getEntry(SERVICE_FILE));
+ Path relocatedJar = temp.newFile("out.jar").toPath();
+ Relocator.run(
+ RelocatorCommand.builder()
+ .addProgramFile(testJarPath)
+ .setOutputPath(relocatedJar)
+ .addPackageMapping(
+ Reference.packageFromString("foo.baz"), Reference.packageFromString("baz.qux"))
+ .build());
+ zip = new ZipFile(relocatedJar.toFile());
+ ZipEntry serviceEntry = zip.getEntry("META-INF/services/foo.bar.Baz");
+ assertNotNull(serviceEntry);
+ InputStream inputStream = zip.getInputStream(serviceEntry);
+ Scanner scanner = new Scanner(inputStream);
+ assertEquals("foo.bar.BazImpl", scanner.next());
+ assertEquals("baz.qux.OtherImpl", scanner.next());
+ assertFalse(scanner.hasNext());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/relocator/RelocatorTest.java b/src/test/java/com/android/tools/r8/relocator/RelocatorTest.java
new file mode 100644
index 0000000..9690ea6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/relocator/RelocatorTest.java
@@ -0,0 +1,350 @@
+// 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.relocator;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertFalse;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundFieldSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import com.android.tools.r8.utils.codeinspector.LocalVariableTable;
+import com.android.tools.r8.utils.codeinspector.LocalVariableTable.LocalVariableTableEntry;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+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 RelocatorTest extends TestBase {
+
+ private final boolean external;
+
+ @Parameters(name = "{0}, external: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(getTestParameters().withNoneRuntime().build(), BooleanUtils.values());
+ }
+
+ public RelocatorTest(TestParameters parameters, boolean external) {
+ this.external = external;
+ }
+
+ @Test
+ public void testRelocatorIdentity()
+ throws IOException, CompilationFailedException, ExecutionException {
+ Path output = temp.newFile("output.jar").toPath();
+ runRelocator(ToolHelper.R8_WITH_DEPS_JAR, new HashMap<>(), output);
+ inspectAllClassesRelocated(ToolHelper.R8_WITH_DEPS_JAR, output, "", "");
+ }
+
+ @Test
+ public void testRelocatorEmptyToSomething() throws Exception {
+ String originalPrefix = "";
+ String newPrefix = "foo.bar.baz";
+ Path output = temp.newFile("output.jar").toPath();
+ Map<String, String> mapping = new HashMap<>();
+ mapping.put(originalPrefix, newPrefix);
+ runRelocator(ToolHelper.R8_WITH_DEPS_JAR, mapping, output);
+ inspectAllClassesRelocated(
+ ToolHelper.R8_WITH_DEPS_JAR, output, originalPrefix, newPrefix + ".");
+ }
+
+ @Test
+ public void testRelocatorSomethingToEmpty() throws IOException {
+ String originalPrefix = "com.android.tools.r8";
+ String newPrefix = "";
+ Path output = temp.newFile("output.jar").toPath();
+ Map<String, String> mapping = new HashMap<>();
+ mapping.put(originalPrefix, newPrefix);
+ // TODO(b/155047618): Fixup the type after rewriting.
+ CompilationFailedException compilationFailedException =
+ assertThrows(
+ CompilationFailedException.class,
+ () -> {
+ runRelocator(ToolHelper.R8_WITH_DEPS_JAR, mapping, output);
+ });
+ }
+
+ @Test
+ public void testRelocateKeepsDebugInfo()
+ throws IOException, CompilationFailedException, ExecutionException {
+ String originalPrefix = "com.android.tools.r8";
+ String newPrefix = "com.android.tools.r8";
+ Path output = temp.newFile("output.jar").toPath();
+ Map<String, String> mapping = new HashMap<>();
+ mapping.put(originalPrefix, newPrefix);
+ runRelocator(ToolHelper.R8_WITH_DEPS_JAR, mapping, output);
+ // Assert that all classes are the same, have the same methods and debug info:
+ CodeInspector originalInspector = new CodeInspector(ToolHelper.R8_WITH_DEPS_JAR);
+ CodeInspector relocatedInspector = new CodeInspector(output);
+ for (FoundClassSubject clazz : originalInspector.allClasses()) {
+ ClassSubject relocatedClass = relocatedInspector.clazz(clazz.getFinalName());
+ assertThat(relocatedClass, isPresent());
+ assertEquals(
+ clazz.getDexProgramClass().sourceFile, relocatedClass.getDexProgramClass().sourceFile);
+ for (FoundMethodSubject originalMethod : clazz.allMethods()) {
+ MethodSubject relocatedMethod = relocatedClass.method(originalMethod.asMethodReference());
+ assertThat(relocatedMethod, isPresent());
+ assertEquals(originalMethod.hasLineNumberTable(), relocatedMethod.hasLineNumberTable());
+ if (originalMethod.hasLineNumberTable()) {
+ // TODO(b/155303677): Figure out why we cannot assert the same lines.
+ // assertEquals(
+ // originalMethod.getLineNumberTable().getLines().size(),
+ // relocatedMethod.getLineNumberTable().getLines().size());
+ }
+ assertEquals(
+ originalMethod.hasLocalVariableTable(), relocatedMethod.hasLocalVariableTable());
+ if (originalMethod.hasLocalVariableTable()) {
+ LocalVariableTable originalVariableTable = originalMethod.getLocalVariableTable();
+ LocalVariableTable relocatedVariableTable = relocatedMethod.getLocalVariableTable();
+ assertEquals(originalVariableTable.size(), relocatedVariableTable.size());
+ for (int i = 0; i < originalVariableTable.getEntries().size(); i++) {
+ LocalVariableTableEntry originalEntry = originalVariableTable.get(i);
+ LocalVariableTableEntry relocatedEntry = relocatedVariableTable.get(i);
+ assertEquals(originalEntry.name, relocatedEntry.name);
+ assertEquals(originalEntry.signature, relocatedEntry.signature);
+ assertEquals(originalEntry.type.toString(), relocatedEntry.type.toString());
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testRelocateAll() throws IOException, CompilationFailedException, ExecutionException {
+ String originalPrefix = "com.android.tools.r8";
+ String newPrefix = "foo.bar.baz";
+ Map<String, String> mapping = new HashMap<>();
+ mapping.put("some.package.that.does.not.exist", "foo");
+ mapping.put(originalPrefix, newPrefix);
+ Path output = temp.newFile("output.jar").toPath();
+ runRelocator(ToolHelper.R8_WITH_DEPS_JAR, mapping, output);
+ inspectAllClassesRelocated(ToolHelper.R8_WITH_DEPS_JAR, output, originalPrefix, newPrefix);
+ }
+
+ @Test
+ public void testOrderingOfPrefixes() throws Exception {
+ String originalPrefix = "com.android";
+ String newPrefix = "foo.bar.baz";
+ Path output = temp.newFile("output.jar").toPath();
+ Map<String, String> mapping = new LinkedHashMap<>();
+ mapping.put(originalPrefix, newPrefix);
+ mapping.put("com.android.tools.r8", "qux");
+ CompilationFailedException exception =
+ assertThrows(
+ CompilationFailedException.class,
+ () -> runRelocator(ToolHelper.R8_WITH_DEPS_JAR, mapping, output));
+ assertThat(
+ exception.getCause().getMessage(),
+ containsString("can be relocated by multiple mappings."));
+ }
+
+ @Test
+ public void testNoReEntry() throws IOException, CompilationFailedException, ExecutionException {
+ // TODO(b/154909222): Check if this is the behavior we would like.
+ String originalPrefix = "com.android";
+ String newPrefix = "foo.bar.baz";
+ Map<String, String> mapping = new LinkedHashMap<>();
+ mapping.put(originalPrefix, newPrefix);
+ mapping.put(newPrefix, "qux");
+ Path output = temp.newFile("output.jar").toPath();
+ runRelocator(ToolHelper.R8_WITH_DEPS_JAR, mapping, output);
+ inspectAllClassesRelocated(ToolHelper.R8_WITH_DEPS_JAR, output, originalPrefix, newPrefix);
+ // Assert that no mappings of com.android.tools.r8 -> qux exists.
+ CodeInspector inspector = new CodeInspector(output);
+ assertFalse(
+ inspector.allClasses().stream().anyMatch(clazz -> clazz.getFinalName().startsWith("qux")));
+ }
+
+ @Test
+ public void testMultiplePackages()
+ throws IOException, ExecutionException, CompilationFailedException {
+ Set<String> seenPackages = new HashSet<>();
+ List<Pair<String, String>> packageMappings = new ArrayList<>();
+ Map<String, String> mapping = new LinkedHashMap<>();
+ CodeInspector inspector = new CodeInspector(ToolHelper.R8_WITH_DEPS_JAR);
+ int packageNameCounter = 0;
+ // Generate a mapping for each package name directly below com.android.tools.r8.
+ for (FoundClassSubject clazz : inspector.allClasses()) {
+ String packageName = clazz.getDexProgramClass().getType().getPackageName();
+ String prefix = "com.android.tools.r8.";
+ if (!packageName.startsWith(prefix)) {
+ continue;
+ }
+ int nextPackageNameIndex = packageName.indexOf('.', prefix.length());
+ if (nextPackageNameIndex > prefix.length()) {
+ String mappedPackageName =
+ prefix + packageName.substring(prefix.length(), nextPackageNameIndex);
+ if (seenPackages.add(mappedPackageName)) {
+ String relocatedPackageName = "number" + packageNameCounter++;
+ packageMappings.add(new Pair<>(mappedPackageName, relocatedPackageName));
+ mapping.put(mappedPackageName, relocatedPackageName);
+ }
+ }
+ }
+ Path output = temp.newFile("output.jar").toPath();
+ runRelocator(ToolHelper.R8_WITH_DEPS_JAR, mapping, output);
+ for (Pair<String, String> packageMapping : packageMappings) {
+ inspectAllClassesRelocated(
+ ToolHelper.R8_WITH_DEPS_JAR,
+ output,
+ packageMapping.getFirst(),
+ packageMapping.getSecond());
+ }
+ }
+
+ @Test
+ public void testPartialPrefix()
+ throws CompilationFailedException, IOException, ExecutionException {
+ String originalPrefix = "com.android.tools.r";
+ String newPrefix = "i_cannot_w";
+ Map<String, String> mapping = new LinkedHashMap<>();
+ mapping.put(originalPrefix, newPrefix);
+ Path output = temp.newFile("output.jar").toPath();
+ runRelocator(ToolHelper.R8_WITH_DEPS_JAR, mapping, output);
+ inspectAllClassesRelocated(ToolHelper.R8_WITH_DEPS_JAR, output, originalPrefix, originalPrefix);
+ }
+
+ @Test
+ public void testBootstrap() throws IOException, CompilationFailedException, ExecutionException {
+ String originalPrefix = "com.android.tools.r8";
+ String newPrefix = "relocated_r8";
+ Map<String, String> mapping = new LinkedHashMap<>();
+ mapping.put(originalPrefix, newPrefix);
+ Path output = temp.newFile("output.jar").toPath();
+ runRelocator(ToolHelper.R8_WITH_DEPS_JAR, mapping, output);
+ // Check that all classes has been remapped.
+ inspectAllClassesRelocated(ToolHelper.R8_WITH_DEPS_JAR, output, originalPrefix, newPrefix);
+ inspectAllSignaturesNotContainingString(output, originalPrefix);
+ // We should be able to call the relocated relocator.
+ Path bootstrapOutput = temp.newFile("bootstrap.jar").toPath();
+ ProcessResult processResult =
+ ToolHelper.runJava(
+ output,
+ newPrefix + ".SwissArmyKnife",
+ "relocator",
+ "--input",
+ output.toString(),
+ "--output",
+ bootstrapOutput.toString(),
+ "--map",
+ newPrefix + "->" + originalPrefix);
+ System.out.println(processResult.stderr);
+ assertEquals(0, processResult.exitCode);
+ inspectAllClassesRelocated(output, bootstrapOutput, newPrefix, originalPrefix);
+ inspectAllSignaturesNotContainingString(bootstrapOutput, newPrefix);
+ // Assert that this is infact an identity transformation.
+ inspectAllClassesRelocated(ToolHelper.R8_WITH_DEPS_JAR, bootstrapOutput, "", "");
+ }
+
+ @Test
+ public void testNest() throws IOException, CompilationFailedException, ExecutionException {
+ String originalPrefix = "com.android.tools.r8";
+ String newPrefix = "com.android.tools.r8";
+ Path output = temp.newFile("output.jar").toPath();
+ Map<String, String> mapping = new HashMap<>();
+ mapping.put(originalPrefix, newPrefix);
+ runRelocator(ToolHelper.R8_WITH_DEPS_11_JAR, mapping, output);
+ // Assert that all classes are the same, have the same methods and nest info.
+ CodeInspector originalInspector = new CodeInspector(ToolHelper.R8_WITH_DEPS_11_JAR);
+ CodeInspector relocatedInspector = new CodeInspector(output);
+ for (FoundClassSubject originalSubject : originalInspector.allClasses()) {
+ ClassSubject relocatedSubject = relocatedInspector.clazz(originalSubject.getFinalName());
+ assertThat(relocatedSubject, isPresent());
+ DexClass originalClass = originalSubject.getDexProgramClass();
+ DexClass relocatedClass = relocatedSubject.getDexProgramClass();
+ assertEquals(originalClass.isNestHost(), relocatedClass.isNestHost());
+ assertEquals(originalClass.isNestMember(), relocatedClass.isNestMember());
+ if (originalClass.isInANest()) {
+ assertEquals(
+ originalClass.getNestHost().descriptor, relocatedClass.getNestHost().descriptor);
+ }
+ }
+ }
+
+ private void runRelocator(Path input, Map<String, String> mapping, Path output)
+ throws CompilationFailedException {
+ if (external) {
+ List<String> args = new ArrayList<>();
+ args.add("--input");
+ args.add(input.toString());
+ args.add("--output");
+ args.add(output.toString());
+ mapping.forEach(
+ (key, value) -> {
+ args.add("--map");
+ args.add(key + "->" + value);
+ });
+ RelocatorCommandLine.run(args.toArray(new String[0]));
+ } else {
+ RelocatorCommand.Builder builder =
+ RelocatorCommand.builder().addProgramFiles(input).setOutputPath(output);
+ mapping.forEach(
+ (key, value) ->
+ builder.addPackageMapping(
+ Reference.packageFromString(key), Reference.packageFromString(value)));
+ Relocator.run(builder.build());
+ }
+ }
+
+ private void inspectAllClassesRelocated(
+ Path original, Path relocated, String originalPrefix, String newPrefix)
+ throws IOException, ExecutionException {
+ CodeInspector originalInspector = new CodeInspector(original);
+ CodeInspector relocatedInspector = new CodeInspector(relocated);
+ for (FoundClassSubject clazz : originalInspector.allClasses()) {
+ if (originalPrefix.isEmpty()
+ || clazz
+ .getFinalName()
+ .startsWith(originalPrefix + DescriptorUtils.JAVA_PACKAGE_SEPARATOR)) {
+ String relocatedName = newPrefix + clazz.getFinalName().substring(originalPrefix.length());
+ ClassSubject relocatedClass = relocatedInspector.clazz(relocatedName);
+ assertThat(relocatedClass, isPresent());
+ }
+ }
+ }
+
+ private void inspectAllSignaturesNotContainingString(Path relocated, String originalPrefix)
+ throws IOException, ExecutionException {
+ CodeInspector relocatedInspector = new CodeInspector(relocated);
+ for (FoundClassSubject clazz : relocatedInspector.allClasses()) {
+ assertThat(clazz.getFinalSignatureAttribute(), not(containsString(originalPrefix)));
+ for (FoundMethodSubject method : clazz.allMethods()) {
+ assertThat(method.getJvmMethodSignatureAsString(), not(containsString(originalPrefix)));
+ }
+ for (FoundFieldSubject field : clazz.allFields()) {
+ assertThat(field.getJvmFieldSignatureAsString(), not(containsString(originalPrefix)));
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/relocator/foo/bar/Baz.java b/src/test/java/com/android/tools/r8/relocator/foo/bar/Baz.java
new file mode 100644
index 0000000..52b573e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/relocator/foo/bar/Baz.java
@@ -0,0 +1,35 @@
+// 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.relocator.foo.bar;
+
+import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
+import static org.objectweb.asm.Opcodes.ACC_INTERFACE;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static org.objectweb.asm.Opcodes.V1_8;
+
+import org.objectweb.asm.ClassWriter;
+
+public interface Baz {
+
+ // This is dump of the empty interface Baz.
+ static byte[] dump() {
+
+ ClassWriter classWriter = new ClassWriter(0);
+
+ classWriter.visit(
+ V1_8,
+ ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
+ "foo/bar/Baz",
+ null,
+ "java/lang/Object",
+ null);
+
+ classWriter.visitSource("Baz.java", null);
+
+ classWriter.visitEnd();
+
+ return classWriter.toByteArray();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/relocator/foo/bar/BazImpl.java b/src/test/java/com/android/tools/r8/relocator/foo/bar/BazImpl.java
new file mode 100644
index 0000000..971b280
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/relocator/foo/bar/BazImpl.java
@@ -0,0 +1,55 @@
+// 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.relocator.foo.bar;
+
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static org.objectweb.asm.Opcodes.ACC_SUPER;
+import static org.objectweb.asm.Opcodes.ALOAD;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.RETURN;
+import static org.objectweb.asm.Opcodes.V1_8;
+
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+
+public class BazImpl implements Baz {
+
+ // This is a dump of the empty BazImpl class.
+ public static byte[] dump() {
+
+ ClassWriter classWriter = new ClassWriter(0);
+ MethodVisitor methodVisitor;
+
+ classWriter.visit(
+ V1_8,
+ ACC_PUBLIC | ACC_SUPER,
+ "foo/bar/BazImpl",
+ null,
+ "java/lang/Object",
+ new String[] {"foo/bar/Baz"});
+
+ classWriter.visitSource("BazImpl.java", null);
+
+ {
+ methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(7, label0);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ methodVisitor.visitInsn(RETURN);
+ Label label1 = new Label();
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitLocalVariable("this", "Lfoo/bar/BazImpl;", null, label0, label1, 0);
+ methodVisitor.visitMaxs(1, 1);
+ methodVisitor.visitEnd();
+ }
+ classWriter.visitEnd();
+
+ return classWriter.toByteArray();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/relocator/foo/baz/OtherImpl.java b/src/test/java/com/android/tools/r8/relocator/foo/baz/OtherImpl.java
new file mode 100644
index 0000000..6242f3c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/relocator/foo/baz/OtherImpl.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.relocator.foo.baz;
+
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static org.objectweb.asm.Opcodes.ACC_SUPER;
+import static org.objectweb.asm.Opcodes.ALOAD;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.RETURN;
+import static org.objectweb.asm.Opcodes.V1_8;
+
+import com.android.tools.r8.relocator.foo.bar.Baz;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+
+public class OtherImpl implements Baz {
+
+ // This is a dump of the empty OtherImpl class.
+ public static byte[] dump() {
+
+ ClassWriter classWriter = new ClassWriter(0);
+ MethodVisitor methodVisitor;
+
+ classWriter.visit(
+ V1_8,
+ ACC_PUBLIC | ACC_SUPER,
+ "foo/baz/OtherImpl",
+ null,
+ "java/lang/Object",
+ new String[] {"foo/bar/Baz"});
+
+ classWriter.visitSource("OtherImpl.java", null);
+
+ {
+ methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+ methodVisitor.visitCode();
+ Label label0 = new Label();
+ methodVisitor.visitLabel(label0);
+ methodVisitor.visitLineNumber(7, label0);
+ methodVisitor.visitVarInsn(ALOAD, 0);
+ methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ methodVisitor.visitInsn(RETURN);
+ Label label1 = new Label();
+ methodVisitor.visitLabel(label1);
+ methodVisitor.visitLocalVariable("this", "Lfoo/baz/OtherImpl;", null, label0, label1, 0);
+ methodVisitor.visitMaxs(1, 1);
+ methodVisitor.visitEnd();
+ }
+ classWriter.visitEnd();
+
+ return classWriter.toByteArray();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/ArrayTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/ArrayTargetLookupTest.java
index 079a8f5..02cb64e 100644
--- a/src/test/java/com/android/tools/r8/resolution/ArrayTargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/ArrayTargetLookupTest.java
@@ -10,7 +10,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
@@ -38,7 +38,7 @@
.build();
DirectMappedDexApplication application =
new ApplicationReader(app, options, timing).read().toDirect();
- AppInfoWithSubtyping appInfo = new AppInfoWithSubtyping(application);
+ AppInfoWithClassHierarchy appInfo = new AppInfoWithClassHierarchy(application);
DexItemFactory factory = options.itemFactory;
DexType fooType =
factory.createType(DescriptorUtils.javaTypeToDescriptor(Foo.class.getTypeName()));
diff --git a/src/test/java/com/android/tools/r8/resolution/InvokeInterfaceOnClassTest.java b/src/test/java/com/android/tools/r8/resolution/InvokeInterfaceOnClassTest.java
index c9d94ad..84a91ca 100644
--- a/src/test/java/com/android/tools/r8/resolution/InvokeInterfaceOnClassTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/InvokeInterfaceOnClassTest.java
@@ -108,14 +108,16 @@
private static byte[] transformMain() throws Exception {
String binaryNameForI = Reference.classFromClass(I.class).getBinaryName();
return transformer(Main.class)
- .transformMethodInsnInMethod("main",
+ .transformMethodInsnInMethod(
+ "main",
(opcode, owner, name, descriptor, isInterface, continuation) -> {
if (owner.equals(binaryNameForI) && name.equals("f")) {
assertEquals(Opcodes.INVOKEVIRTUAL, opcode);
assertFalse(isInterface);
- continuation.apply(Opcodes.INVOKEINTERFACE, owner, name, descriptor, true);
+ continuation.visitMethodInsn(
+ Opcodes.INVOKEINTERFACE, owner, name, descriptor, true);
} else {
- continuation.apply(opcode, owner, name, descriptor, isInterface);
+ continuation.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
})
.transform();
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 8fccb56..bade483 100644
--- a/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/InvokeSuperCallInStaticTest.java
@@ -97,7 +97,7 @@
.transformMethodInsnInMethod(
"callSuper",
(opcode, owner, name, descriptor, isInterface, continuation) -> {
- continuation.apply(
+ continuation.visitMethodInsn(
INVOKESPECIAL,
DescriptorUtils.getBinaryNameFromJavaType(Base.class.getTypeName()),
name,
diff --git a/src/test/java/com/android/tools/r8/resolution/InvokeVirtualOnInterfaceTest.java b/src/test/java/com/android/tools/r8/resolution/InvokeVirtualOnInterfaceTest.java
index d0a4906..3f2fc61 100644
--- a/src/test/java/com/android/tools/r8/resolution/InvokeVirtualOnInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/InvokeVirtualOnInterfaceTest.java
@@ -115,9 +115,9 @@
if (owner.equals(binaryNameForI) && name.equals("f")) {
assertEquals(INVOKEINTERFACE, opcode);
assertTrue(isInterface);
- continuation.apply(INVOKEVIRTUAL, owner, name, descriptor, false);
+ continuation.visitMethodInsn(INVOKEVIRTUAL, owner, name, descriptor, false);
} else {
- continuation.apply(opcode, owner, name, descriptor, isInterface);
+ continuation.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
})
.transform();
diff --git a/src/test/java/com/android/tools/r8/resolution/PrivateInvokeVirtualTest.java b/src/test/java/com/android/tools/r8/resolution/PrivateInvokeVirtualTest.java
index 543ecad..c0f46b5 100644
--- a/src/test/java/com/android/tools/r8/resolution/PrivateInvokeVirtualTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/PrivateInvokeVirtualTest.java
@@ -85,9 +85,10 @@
(opcode, owner, name, descriptor, isInterface, continuation) -> {
if (name.contains("compareToHelper")) {
assertEquals(Opcodes.INVOKESPECIAL, opcode);
- continuation.apply(Opcodes.INVOKEVIRTUAL, owner, name, descriptor, isInterface);
+ continuation.visitMethodInsn(
+ Opcodes.INVOKEVIRTUAL, owner, name, descriptor, isInterface);
} else {
- continuation.apply(opcode, owner, name, descriptor, isInterface);
+ continuation.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
})
.transform();
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 18cbb66..67bf3e3 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
@@ -23,6 +23,7 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -85,7 +86,7 @@
? DescriptorUtils.getBinaryNameFromJavaType(I.class.getName())
: DescriptorUtils.getBinaryNameFromJavaType(A.class.getName());
boolean newIsInterface = symbolicReferenceIsDefiningType;
- continuation.apply(
+ continuation.visitMethodInsn(
Opcodes.INVOKESPECIAL, newOwner, name, descriptor, newIsInterface);
})
.transform());
@@ -127,7 +128,9 @@
return;
}
- assertEquals(inSameNest, resolutionResult.isAccessibleFrom(callerClassDefinition, appInfo));
+ assertEquals(
+ OptionalBool.of(inSameNest),
+ resolutionResult.isAccessibleFrom(callerClassDefinition, appInfo));
DexEncodedMethod targetSpecial =
resolutionResult.lookupInvokeSpecialTarget(callerClassDefinition, appInfo);
DexEncodedMethod targetSuper =
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 bae2756..a25f3c6 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
@@ -83,7 +83,8 @@
symbolicReferenceIsDefiningType
? DescriptorUtils.getBinaryNameFromJavaType(I.class.getName())
: DescriptorUtils.getBinaryNameFromJavaType(J.class.getName());
- continuation.apply(Opcodes.INVOKESPECIAL, newOwner, name, descriptor, true);
+ continuation.visitMethodInsn(
+ Opcodes.INVOKESPECIAL, newOwner, name, descriptor, true);
})
.transform());
}
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 58575d8..026055c 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
@@ -21,6 +21,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -101,7 +102,9 @@
definingClassDefinition, resolutionResult.asSingleResolution().getResolvedHolder());
// Verify that the resolved method is accessible if in the same nest.
- assertEquals(inSameNest, resolutionResult.isAccessibleFrom(callerClassDefinition, appInfo));
+ assertEquals(
+ OptionalBool.of(inSameNest),
+ resolutionResult.isAccessibleFrom(callerClassDefinition, appInfo));
// Verify that looking up the dispatch target returns the defining method.
DexEncodedMethod targetSpecial =
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 79e8f76..f1e182a 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
@@ -24,6 +24,7 @@
import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -86,7 +87,7 @@
symbolicReferenceIsDefiningType
? DescriptorUtils.getBinaryNameFromJavaType(A.class.getName())
: DescriptorUtils.getBinaryNameFromJavaType(B.class.getName());
- continuation.apply(opcode, newOwner, name, descriptor, isInterface);
+ continuation.visitMethodInsn(opcode, newOwner, name, descriptor, isInterface);
})
.transform());
}
@@ -133,7 +134,9 @@
definingClassDefinition, resolutionResult.asSingleResolution().getResolvedHolder());
// Verify that the resolved method is accessible only when in the same nest.
- assertEquals(inSameNest, resolutionResult.isAccessibleFrom(callerClassDefinition, appInfo));
+ assertEquals(
+ OptionalBool.of(inSameNest),
+ resolutionResult.isAccessibleFrom(callerClassDefinition, appInfo));
// Verify that looking up the dispatch target returns a valid target
// iff in the same nest and declaredHolder == definingHolder.
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 db75b6e..4f013bb 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
@@ -20,6 +20,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -103,7 +104,8 @@
definingClassDefinition, resolutionResult.asSingleResolution().getResolvedHolder());
// Verify that the resolved method is accessible (it is public).
- assertTrue(resolutionResult.isAccessibleFrom(callerClassDefinition, appInfo));
+ assertEquals(
+ OptionalBool.TRUE, resolutionResult.isAccessibleFrom(callerClassDefinition, appInfo));
// Verify that looking up the dispatch target returns the defining method.
DexEncodedMethod targetSpecial =
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 5c1d7a8..15f7edc 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
@@ -17,6 +17,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
@@ -70,7 +71,7 @@
appInfo.definitionFor(buildType(B.class, appInfo.dexItemFactory())).asProgramClass();
DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory());
ResolutionResult resolutionResult = appInfo.resolveMethod(bar.holder, bar);
- assertEquals(inSameNest, resolutionResult.isAccessibleFrom(bClass, appInfo));
+ assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appInfo));
}
@Test
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 e3ca436..4041e47 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
@@ -17,6 +17,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
@@ -72,7 +73,7 @@
appInfo.definitionFor(buildType(B.class, appInfo.dexItemFactory())).asProgramClass();
DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory());
ResolutionResult resolutionResult = appInfo.resolveMethod(bar.holder, bar);
- assertEquals(inSameNest, resolutionResult.isAccessibleFrom(bClass, appInfo));
+ assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appInfo));
}
@Test
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 f1c5fa1..8dc4191 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
@@ -17,6 +17,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
@@ -72,7 +73,7 @@
appInfo.definitionFor(buildType(B.class, appInfo.dexItemFactory())).asProgramClass();
DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory());
ResolutionResult resolutionResult = appInfo.resolveMethod(bar.holder, bar);
- assertEquals(inSameNest, resolutionResult.isAccessibleFrom(bClass, appInfo));
+ assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appInfo));
}
@Test
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 cdf2b22..d6e6ab9 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
@@ -16,6 +16,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.transformers.ClassFileTransformer;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
@@ -71,7 +72,7 @@
appInfo.definitionFor(buildType(B.class, appInfo.dexItemFactory())).asProgramClass();
DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory());
ResolutionResult resolutionResult = appInfo.resolveMethod(bar.holder, bar);
- assertEquals(inSameNest, resolutionResult.isAccessibleFrom(bClass, appInfo));
+ assertEquals(OptionalBool.of(inSameNest), resolutionResult.isAccessibleFrom(bClass, appInfo));
}
@Test
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 f2f3309..020ec02 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
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.resolution.access;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
@@ -56,7 +57,7 @@
appInfo.definitionFor(buildType(A.class, appInfo.dexItemFactory())).asProgramClass();
DexMethod bar = buildMethod(A.class.getDeclaredMethod("bar"), appInfo.dexItemFactory());
ResolutionResult resolutionResult = appInfo.resolveMethod(bar.holder, bar);
- assertTrue(resolutionResult.isAccessibleFrom(aClass, appInfo));
+ assertEquals(OptionalBool.TRUE, resolutionResult.isAccessibleFrom(aClass, appInfo));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/resolution/access/indirectfield/IndirectFieldAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/indirectfield/IndirectFieldAccessTest.java
index 94b3c45..4616af8 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/indirectfield/IndirectFieldAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/indirectfield/IndirectFieldAccessTest.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.resolution.access.indirectfield;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -18,6 +18,7 @@
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.resolution.access.indirectfield.pkg.C;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.util.List;
@@ -60,7 +61,8 @@
DexClass initialResolutionHolder = appInfo.definitionFor(f.holder);
DexEncodedField resolutionTarget = appInfo.resolveField(f);
// TODO(b/145723539): Test access via the resolution result once possible.
- assertTrue(
+ assertEquals(
+ OptionalBool.TRUE,
AccessControl.isFieldAccessible(
resolutionTarget, initialResolutionHolder, cClass, 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 34af60e..bdd8484 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
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.resolution.access.indirectmethod;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.resolution.access.indirectmethod.pkg.C;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
@@ -61,7 +62,8 @@
appInfo.definitionFor(buildType(C.class, appInfo.dexItemFactory())).asProgramClass();
DexMethod bar = buildMethod(B.class.getMethod("foo"), appInfo.dexItemFactory());
ResolutionResult resolutionResult = appInfo.resolveMethod(bar.holder, bar);
- assertTrue(resolutionResult.isAccessibleForVirtualDispatchFrom(cClass, appInfo));
+ assertEquals(
+ OptionalBool.TRUE, resolutionResult.isAccessibleForVirtualDispatchFrom(cClass, appInfo));
}
@Test
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 1e9ca71..440e077 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
@@ -64,7 +64,7 @@
assertTrue(lookupResult.isLookupResultSuccess());
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected =
ImmutableSet.of(
A.class.getTypeName() + ".bar",
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 ccb80a5..edbb580 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
@@ -62,7 +62,7 @@
assertTrue(lookupResult.isLookupResultSuccess());
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected =
ImmutableSet.of(A.class.getTypeName() + ".bar", I.class.getTypeName() + ".bar");
assertEquals(expected, targets);
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 b3aae9e..e59009e 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
@@ -66,7 +66,7 @@
assertTrue(lookupResult.isLookupResultSuccess());
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
assertEquals(expected, targets);
}
@@ -109,7 +109,7 @@
assertTrue(lookupResult.isLookupResultSuccess());
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
assertEquals(expected, targets);
}
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 7c50e54..cb44081 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
@@ -64,7 +64,7 @@
assertTrue(lookupResult.isLookupResultSuccess());
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
assertEquals(expected, targets);
}
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceClInitTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceClInitTest.java
index 06f7e8d..9a17065 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceClInitTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceClInitTest.java
@@ -132,7 +132,7 @@
.transformMethodInsnInMethod(
"callClInit",
(opcode, owner, name, descriptor, isInterface, continuation) ->
- continuation.apply(opcode, owner, "<clinit>", descriptor, isInterface))
+ continuation.visitMethodInsn(opcode, owner, "<clinit>", descriptor, isInterface))
.transform();
}
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
index c8fad67..81ed494 100644
--- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
@@ -94,14 +94,14 @@
"callFooBar",
(opcode, owner, name, descriptor, isInterface, continuation) -> {
if (name.equals("notify")) {
- continuation.apply(
+ continuation.visitMethodInsn(
INVOKEINTERFACE,
DescriptorUtils.getBinaryNameFromJavaType(I.class.getTypeName()),
"bar",
descriptor,
true);
} else {
- continuation.apply(opcode, owner, name, descriptor, isInterface);
+ continuation.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
})
.transform();
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 9cf0cf2..0702c97 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
@@ -63,7 +63,7 @@
assertTrue(lookupResult.isLookupResultSuccess());
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected =
ImmutableSet.of(A.class.getTypeName() + ".bar", J.class.getTypeName() + ".bar");
assertEquals(expected, targets);
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 0013abb..5ad3690 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
@@ -63,7 +63,7 @@
assertTrue(lookupResult.isLookupResultSuccess());
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected =
ImmutableSet.of(B.class.getTypeName() + ".foo", C.class.getTypeName() + ".foo");
assertEquals(expected, targets);
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 fbe03e3..4d195e1 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
@@ -67,7 +67,7 @@
lookupResult
.asLookupResultSuccess()
.forEach(
- target -> targets.add(target.getMethod().qualifiedName()),
+ target -> targets.add(target.getDefinition().qualifiedName()),
lambda -> {
assert false;
});
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 4ccdb10..51727c7 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
@@ -67,7 +67,7 @@
lookupResult
.asLookupResultSuccess()
.forEach(
- target -> targets.add(target.getMethod().qualifiedName()),
+ target -> targets.add(target.getDefinition().qualifiedName()),
lambda -> {
fail();
});
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 d5e8bb3..9c02be6 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
@@ -66,7 +66,7 @@
lookupResult
.asLookupResultSuccess()
.forEach(
- target -> targets.add(target.getMethod().qualifiedName()),
+ target -> targets.add(target.getDefinition().qualifiedName()),
lambda -> {
fail();
});
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 23f1c7b..d5d1ce8 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
@@ -66,7 +66,7 @@
lookupResult
.asLookupResultSuccess()
.forEach(
- target -> targets.add(target.getMethod().qualifiedName()),
+ target -> targets.add(target.getDefinition().qualifiedName()),
lambda -> {
fail();
});
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 379b6e8..f0b55f2 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
@@ -75,7 +75,7 @@
assertTrue(lookupResult.isLookupResultSuccess());
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(C.class.getTypeName() + ".foo");
assertEquals(expected, targets);
}
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 b5f36aa..01d5afb 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
@@ -63,7 +63,7 @@
assertTrue(lookupResult.isLookupResultSuccess());
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(D.class.getTypeName() + ".bar");
assertEquals(expected, targets);
}
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 2305c93..37d915a 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
@@ -67,7 +67,7 @@
assertTrue(lookupResult.isLookupResultSuccess());
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected =
ImmutableSet.of(
D.class.getTypeName() + ".bar");
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 e156624..e8c34d1 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
@@ -79,7 +79,7 @@
assertTrue(lookupResult.isLookupResultSuccess());
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
// TODO(b/148591377): The set should be empty.
ImmutableSet<String> expected = ImmutableSet.of(AbstractWidening.class.getTypeName() + ".foo");
assertEquals(expected, targets);
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 d711ba7..338d00a 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
@@ -78,7 +78,7 @@
assertTrue(lookupResult.isLookupResultSuccess());
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
// TODO(b/148591377): The set should be empty.
ImmutableSet<String> expected = ImmutableSet.of(Abstract.class.getTypeName() + ".foo");
assertEquals(expected, targets);
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 ec013d6..e7d36e9 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
@@ -66,7 +66,7 @@
lookupResult
.asLookupResultSuccess()
.forEach(
- target -> targets.add(target.getMethod().qualifiedName()),
+ target -> targets.add(target.getDefinition().qualifiedName()),
lambda -> {
fail();
});
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 8a35389..c15c379 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
@@ -112,7 +112,7 @@
lookupResult
.asLookupResultSuccess()
.forEach(
- clazzAndMethod -> actual.add(clazzAndMethod.getMethod().method),
+ clazzAndMethod -> actual.add(clazzAndMethod.getDefinition().method),
lambdaTarget -> {
assert false;
});
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 ebbb52f..750c642 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
@@ -63,7 +63,7 @@
assertTrue(lookupResult.isLookupResultSuccess());
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected =
ImmutableSet.of(
A.class.getTypeName() + ".foo",
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 4f572ee..8bc102b 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
@@ -63,7 +63,7 @@
assertTrue(lookupResult.isLookupResultSuccess());
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
assertEquals(expected, targets);
}
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 55d4138..469c0ad 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
@@ -63,7 +63,7 @@
assertTrue(lookupResult.isLookupResultSuccess());
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
assertEquals(expected, targets);
}
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 2800d2c..f025e17 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
@@ -66,7 +66,7 @@
assertTrue(lookupResult.isLookupResultSuccess());
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(J.class.getTypeName() + ".foo");
assertEquals(expected, targets);
}
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 d909591..d8f8c4a 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
@@ -102,14 +102,14 @@
"main",
(opcode, owner, name, descriptor, isInterface, continuation) -> {
if (name.equals("foo")) {
- continuation.apply(
+ continuation.visitMethodInsn(
opcode,
DescriptorUtils.getBinaryNameFromJavaType(A.class.getTypeName()),
name,
descriptor,
isInterface);
} else {
- continuation.apply(opcode, owner, name, descriptor, isInterface);
+ continuation.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
})
.transform();
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 b2c70f9..472f7a1 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
@@ -62,7 +62,7 @@
assertTrue(lookupResult.isLookupResultSuccess());
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(I.class.getTypeName() + ".foo");
assertEquals(expected, targets);
}
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 b2e57b2..d3c1b7a 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
@@ -11,7 +11,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -94,7 +94,7 @@
LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
Set<String> expected =
expectedMethodHolders.stream()
.map(c -> c.getTypeName() + ".foo")
@@ -227,11 +227,11 @@
//
// ----- Program -----
// B extends A { } <-- initial
- AppView<AppInfoWithSubtyping> appView =
+ AppView<AppInfoWithClassHierarchy> appView =
computeAppViewWithSubtyping(
buildClasses(Collections.singletonList(B.class), Arrays.asList(A.class, I.class))
.build());
- AppInfoWithSubtyping appInfo = appView.appInfo();
+ AppInfoWithClassHierarchy appInfo = appView.appInfo();
DexMethod method = buildNullaryVoidMethod(B.class, "foo", appInfo.dexItemFactory());
ResolutionResult resolutionResult = appInfo.resolveMethod(method.holder, method);
DexType typeA = buildType(A.class, appInfo.dexItemFactory());
@@ -251,7 +251,7 @@
LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
Set<String> expected = ImmutableSet.of(A.class.getTypeName() + ".foo");
assertEquals(expected, targets);
assertTrue(lookupResultSuccess.isComplete());
@@ -274,7 +274,7 @@
LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
Set<String> expected = ImmutableSet.of(Unrelated.class.getTypeName() + ".foo");
assertEquals(expected, targets);
assertTrue(lookupResultSuccess.isIncomplete());
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 e0f4416..477bed8 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
@@ -68,7 +68,7 @@
assertTrue(lookupResult.isLookupResultSuccess());
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(Middle.class.getTypeName() + ".clear");
assertEquals(expected, targets);
}
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 03425c6..b9f8c90 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
@@ -76,7 +76,7 @@
assertTrue(lookupResult.isLookupResultSuccess());
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(ViewModel.class.getTypeName() + ".clear");
assertEquals(expected, targets);
}
@@ -170,7 +170,7 @@
assertTrue(lookupResult.isLookupResultSuccess());
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(ViewModel.class.getTypeName() + ".clear");
assertEquals(expected, targets);
}
@@ -209,14 +209,14 @@
"main",
(opcode, owner, name, descriptor, isInterface, continuation) -> {
if (name.equals("clear")) {
- continuation.apply(
+ continuation.visitMethodInsn(
opcode,
DescriptorUtils.getBinaryNameFromJavaType(ViewModel.class.getTypeName()),
name,
descriptor,
isInterface);
} else {
- continuation.apply(opcode, owner, name, descriptor, isInterface);
+ continuation.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
})
.transform();
@@ -230,9 +230,9 @@
"run",
(opcode, owner, name, descriptor, isInterface, continuation) -> {
if (name.equals("clearBridge")) {
- continuation.apply(opcode, owner, "clear", descriptor, isInterface);
+ continuation.visitMethodInsn(opcode, owner, "clear", descriptor, isInterface);
} else {
- continuation.apply(opcode, owner, name, descriptor, isInterface);
+ continuation.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
})
.transform();
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 645fb55..c4fb6d2 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
@@ -67,7 +67,7 @@
assertTrue(lookupResult.isLookupResultSuccess());
Set<String> targets = new HashSet<>();
lookupResult.forEach(
- target -> targets.add(target.getMethod().qualifiedName()), lambda -> fail());
+ target -> targets.add(target.getDefinition().qualifiedName()), lambda -> fail());
ImmutableSet<String> expected = ImmutableSet.of(A.class.getTypeName() + ".bar");
assertEquals(expected, targets);
}
@@ -102,11 +102,11 @@
"callOnB",
(opcode, owner, name, descriptor, isInterface, continuation) -> {
if (name.equals("foo")) {
- continuation.apply(opcode, owner, name, descriptor, isInterface);
+ continuation.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
return;
}
if (modifyOwner.get()) {
- continuation.apply(
+ continuation.visitMethodInsn(
Opcodes.INVOKEVIRTUAL,
DescriptorUtils.getBinaryNameFromJavaType(A.class.getTypeName()),
name,
@@ -114,7 +114,8 @@
isInterface);
modifyOwner.set(false);
} else {
- continuation.apply(Opcodes.INVOKEVIRTUAL, owner, name, descriptor, isInterface);
+ continuation.visitMethodInsn(
+ Opcodes.INVOKEVIRTUAL, owner, name, descriptor, isInterface);
}
})
.transform();
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 628c927..b6f838b 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
@@ -67,7 +67,7 @@
lookupResult
.asLookupResultSuccess()
.forEach(
- target -> targets.add(target.getMethod().qualifiedName()),
+ target -> targets.add(target.getDefinition().qualifiedName()),
lambda -> {
fail();
});
diff --git a/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderRewritingTest.java b/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderRewritingTest.java
index 4749163..5f46a45 100644
--- a/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderRewritingTest.java
@@ -168,7 +168,7 @@
// Check that we have removed the service configuration from META-INF/services.
ZipFile zip = new ZipFile(path.toFile());
- assertNull(zip.getEntry("META-INF/services"));
+ assertNull(zip.getEntry("META-INF/services/" + Service.class.getTypeName()));
}
@Test
@@ -197,7 +197,7 @@
// Check that we have removed the service configuration from META-INF/services.
ZipFile zip = new ZipFile(path.toFile());
- assertNull(zip.getEntry("META-INF/services"));
+ assertNull(zip.getEntry("META-INF/services/" + Service.class.getTypeName()));
}
@Test
@@ -226,7 +226,7 @@
// Check that we have removed the service configuration from META-INF/services.
ZipFile zip = new ZipFile(path.toFile());
- assertNull(zip.getEntry("META-INF/services"));
+ assertNull(zip.getEntry("META-INF/services/" + Service.class.getTypeName()));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/shaking/AsterisksTest.java b/src/test/java/com/android/tools/r8/shaking/AsterisksTest.java
index 795ec4d..7261198 100644
--- a/src/test/java/com/android/tools/r8/shaking/AsterisksTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/AsterisksTest.java
@@ -99,7 +99,7 @@
ClassSubject classSubject = codeInspector.clazz(B111974287.class);
assertThat(classSubject, isPresent());
assertThat(classSubject, not(isRenamed()));
- DexClass clazz = classSubject.getDexClass();
+ DexClass clazz = classSubject.getDexProgramClass();
assertEquals(3, clazz.virtualMethods().size());
for (DexEncodedMethod encodedMethod : clazz.virtualMethods()) {
assertTrue(encodedMethod.method.name.toString().startsWith("foo"));
@@ -140,7 +140,7 @@
ClassSubject classSubject = codeInspector.clazz(B111974287.class);
assertThat(classSubject, isPresent());
assertThat(classSubject, not(isRenamed()));
- DexClass clazz = classSubject.getDexClass();
+ DexClass clazz = classSubject.getDexProgramClass();
assertEquals(3, clazz.virtualMethods().size());
for (DexEncodedMethod encodedMethod : clazz.virtualMethods()) {
assertTrue(encodedMethod.method.name.toString().startsWith("foo"));
@@ -162,7 +162,7 @@
ClassSubject classSubject = codeInspector.clazz(B111974287.class);
assertThat(classSubject, isPresent());
assertThat(classSubject, not(isRenamed()));
- DexClass clazz = classSubject.getDexClass();
+ DexClass clazz = classSubject.getDexProgramClass();
assertEquals(3, clazz.virtualMethods().size());
for (DexEncodedMethod encodedMethod : clazz.virtualMethods()) {
assertTrue(encodedMethod.method.name.toString().startsWith("foo"));
diff --git a/src/test/java/com/android/tools/r8/shaking/B149831282.java b/src/test/java/com/android/tools/r8/shaking/B149831282.java
index 910d44b..97441cc 100644
--- a/src/test/java/com/android/tools/r8/shaking/B149831282.java
+++ b/src/test/java/com/android/tools/r8/shaking/B149831282.java
@@ -63,13 +63,14 @@
"main",
(opcode, type, continuation) -> {
assertEquals(binaryName(C.class), type);
- continuation.apply(opcode, "b149831282/C");
+ continuation.visitTypeInsn(opcode, "b149831282/C");
})
.transformMethodInsnInMethod(
"main",
(opcode, owner, name, descriptor, isInterface, continuation) -> {
assertEquals(binaryName(C.class), owner);
- continuation.apply(opcode, "b149831282/C", name, descriptor, isInterface);
+ continuation.visitMethodInsn(
+ opcode, "b149831282/C", name, descriptor, isInterface);
})
.transform())
.addProgramClassFileData(
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index 0b246a5..8365345 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -135,8 +135,10 @@
}
protected static void checkSameStructure(CodeInspector ref, CodeInspector inspector) {
- ref.forAllClasses(refClazz -> checkSameStructure(refClazz,
- inspector.clazz(refClazz.getDexClass().toSourceString())));
+ ref.forAllClasses(
+ refClazz ->
+ checkSameStructure(
+ refClazz, inspector.clazz(refClazz.getDexProgramClass().toSourceString())));
}
private static void checkSameStructure(ClassSubject refClazz, ClassSubject clazz) {
@@ -149,7 +151,10 @@
MethodSignature signature = refMethod.getOriginalSignature();
// Don't check for existence of class initializers, as the code optimization can remove them.
if (!refMethod.isClassInitializer()) {
- Assert.assertTrue("Missing Method: " + clazz.getDexClass().toSourceString() + "."
+ Assert.assertTrue(
+ "Missing Method: "
+ + clazz.getDexProgramClass().toSourceString()
+ + "."
+ signature.toString(),
clazz.method(signature).isPresent());
}
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/PrunedOrMergedAnnotationTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/PrunedOrMergedAnnotationTest.java
index 9b11b86..16c7d4f 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/PrunedOrMergedAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/PrunedOrMergedAnnotationTest.java
@@ -59,7 +59,7 @@
.inspect(
inspector -> {
assertThat(inspector.clazz(A.class), not(isPresent()));
- DexType mergedType = inspector.clazz(B.class).getDexClass().type;
+ DexType mergedType = inspector.clazz(B.class).getDexProgramClass().type;
ClassSubject classC = inspector.clazz(C.class);
assertThat(classC, isPresent());
DexEncodedAnnotation annotation =
diff --git a/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesDotsTest.java b/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesDotsTest.java
index 5a978c6..1caf4f2 100644
--- a/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesDotsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesDotsTest.java
@@ -69,12 +69,14 @@
private void inspect(CodeInspector inspector) {
ClassSubject clazz = inspector.clazz(Main.class);
- assertTrue(clazz.getDexClass().annotations().isEmpty());
+ assertTrue(clazz.getDexProgramClass().annotations().isEmpty());
MethodSubject main = clazz.uniqueMethodWithName("main");
assertTrue(main.getMethod().annotations().isEmpty());
FieldSubject field = clazz.uniqueFieldWithName("field");
assertTrue(field.getField().annotations().isEmpty());
- assertTrue(clazz.getDexClass().sourceFile == null || clazz.getDexClass().sourceFile.size == 0);
+ assertTrue(
+ clazz.getDexProgramClass().sourceFile == null
+ || clazz.getDexProgramClass().sourceFile.size == 0);
assertNull(main.getLineNumberTable());
assertTrue(main.getLocalVariableTable().isEmpty());
}
diff --git a/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java b/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java
index 4ce41ce..29ab0f2 100644
--- a/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.shaking.b134858535;
-import static org.hamcrest.CoreMatchers.containsString;
-
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -22,14 +20,14 @@
@Test
public void testPrivateMethodsInLambdaClass() throws CompilationFailedException {
+ // TODO(b/155534905): Update expectation.
testForR8(Backend.DEX)
.addProgramClasses(Main.class, Interface.class)
.addProgramClassFileData(EventPublisher$bDump.dump())
.addKeepClassRules(Interface.class)
.addKeepMainRule(Main.class)
- .allowDiagnosticInfoMessages()
.setMinApi(AndroidApiLevel.L)
- .compile()
- .assertAllWarningMessagesMatch(containsString("Unrecognized Kotlin lambda"));
+ .compile();
+ // .assertAllWarningMessagesMatch(containsString("Unrecognized Kotlin lambda"));
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking15Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking15Test.java
index 49af47a..2b5cd16 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking15Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking15Test.java
@@ -55,7 +55,7 @@
ImmutableList.of("pqr", "vw$", "abc", "def", "stu", "ghi", "jkl", "ea", "xyz_", "mno");
private static void checkClassAndMemberInDictionary(ClassSubject clazz) {
- String name = clazz.getDexClass().type.getName();
+ String name = clazz.getDexProgramClass().type.getName();
if (!names.contains(name) && !name.equals("Shaking")) {
throw new AssertionError();
}
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAnnotationremovalTest.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAnnotationremovalTest.java
index 52a756b..198b253 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAnnotationremovalTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAnnotationremovalTest.java
@@ -70,25 +70,25 @@
private void annotationRemovalHasNoInnerClassAnnotations(CodeInspector inspector) {
ClassSubject outer = inspector.clazz("annotationremoval.OuterClass");
Assert.assertTrue(outer.isPresent());
- Assert.assertTrue(outer.getDexClass().getInnerClasses().isEmpty());
+ Assert.assertTrue(outer.getDexProgramClass().getInnerClasses().isEmpty());
ClassSubject inner = inspector.clazz("annotationremoval.OuterClass$InnerClass");
Assert.assertTrue(inner.isPresent());
- Assert.assertNull(inner.getDexClass().getEnclosingMethod());
- Assert.assertTrue(inner.getDexClass().getInnerClasses().isEmpty());
+ Assert.assertNull(inner.getDexProgramClass().getEnclosingMethod());
+ Assert.assertTrue(inner.getDexProgramClass().getInnerClasses().isEmpty());
ClassSubject anonymous = inspector.clazz("annotationremoval.OuterClass$1");
Assert.assertTrue(anonymous.isPresent());
- Assert.assertNull(anonymous.getDexClass().getEnclosingMethod());
- Assert.assertTrue(anonymous.getDexClass().getInnerClasses().isEmpty());
+ Assert.assertNull(anonymous.getDexProgramClass().getEnclosingMethod());
+ Assert.assertTrue(anonymous.getDexProgramClass().getInnerClasses().isEmpty());
ClassSubject local = inspector.clazz("annotationremoval.OuterClass$1LocalMagic");
Assert.assertTrue(local.isPresent());
- Assert.assertNull(local.getDexClass().getEnclosingMethod());
- Assert.assertTrue(local.getDexClass().getInnerClasses().isEmpty());
+ Assert.assertNull(local.getDexProgramClass().getEnclosingMethod());
+ Assert.assertTrue(local.getDexProgramClass().getInnerClasses().isEmpty());
}
private void annotationRemovalHasAllInnerClassAnnotations(CodeInspector inspector) {
ClassSubject outer = inspector.clazz("annotationremoval.OuterClass");
Assert.assertTrue(outer.isPresent());
- Assert.assertFalse(outer.getDexClass().getInnerClasses().isEmpty());
+ Assert.assertFalse(outer.getDexProgramClass().getInnerClasses().isEmpty());
ClassSubject inner = inspector.clazz("annotationremoval.OuterClass$InnerClass");
Assert.assertTrue(inner.isPresent());
Assert.assertTrue(inner.isMemberClass());
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index ee8629f..91cd90c 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -119,8 +119,7 @@
// The test contains only a member class so the enclosing-method attribute will be null.
assertEquals(
- forceProguardCompatibility,
- !clazz.getDexClass().getInnerClasses().isEmpty());
+ forceProguardCompatibility, !clazz.getDexProgramClass().getInnerClasses().isEmpty());
assertEquals(forceProguardCompatibility || keepAnnotations,
clazz.annotation(annotationClass.getCanonicalName()).isPresent());
}
@@ -447,9 +446,9 @@
ClassSubject clazz = inspector.clazz(TestKeepAttributes.class);
assertThat(clazz, isPresent());
if (innerClasses || enclosingMethod) {
- assertFalse(clazz.getDexClass().getInnerClasses().isEmpty());
+ assertFalse(clazz.getDexProgramClass().getInnerClasses().isEmpty());
} else {
- assertTrue(clazz.getDexClass().getInnerClasses().isEmpty());
+ assertTrue(clazz.getDexProgramClass().getInnerClasses().isEmpty());
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/ConsequentRootSetWithSatisfiedDependentItemsTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/ConsequentRootSetWithSatisfiedDependentItemsTest.java
index f65d00b..040b2d4 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/ConsequentRootSetWithSatisfiedDependentItemsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/ConsequentRootSetWithSatisfiedDependentItemsTest.java
@@ -50,7 +50,7 @@
private void inspect(CodeInspector inspector) {
ClassSubject aClassSubject = inspector.clazz(A.class);
assertThat(aClassSubject, isPresent());
- assertFalse(aClassSubject.getDexClass().isAbstract());
+ assertFalse(aClassSubject.getDexProgramClass().isAbstract());
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/ExtendsMergedTypeDirectlyTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/ExtendsMergedTypeDirectlyTest.java
index caa6f59..b6ab4b4 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/ExtendsMergedTypeDirectlyTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/ExtendsMergedTypeDirectlyTest.java
@@ -51,7 +51,8 @@
// Check that TestClass no longer extends C.
ClassSubject testClassSubject = inspector.clazz(TestClass.class);
assertThat(testClassSubject, isPresent());
- assertEquals("java.lang.Object", testClassSubject.getDexClass().superType.toSourceString());
+ assertEquals(
+ "java.lang.Object", testClassSubject.getDexProgramClass().superType.toSourceString());
// Check that C is no longer present.
assertThat(inspector.clazz(C.class), not(isPresent()));
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/ExtendsMergedTypeIndirectlyTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/ExtendsMergedTypeIndirectlyTest.java
index 7f6a1c6..ad6678c 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/ExtendsMergedTypeIndirectlyTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/ExtendsMergedTypeIndirectlyTest.java
@@ -47,6 +47,7 @@
// Verify that TestClass still inherits from B.
ClassSubject testClassSubject = inspector.clazz(TestClass.class);
- assertEquals(B.class.getTypeName(), testClassSubject.getDexClass().superType.toSourceString());
+ assertEquals(
+ B.class.getTypeName(), testClassSubject.getDexProgramClass().superType.toSourceString());
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/ImplementsMergedTypeDirectlyTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/ImplementsMergedTypeDirectlyTest.java
index ec06ebc..5c23b99 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/ImplementsMergedTypeDirectlyTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/ImplementsMergedTypeDirectlyTest.java
@@ -62,7 +62,7 @@
// Check that TestClass no longer implements K.
ClassSubject testClassSubject = inspector.clazz(TestClass.class);
assertThat(testClassSubject, isPresent());
- assertTrue(testClassSubject.getDexClass().interfaces.isEmpty());
+ assertTrue(testClassSubject.getDexProgramClass().interfaces.isEmpty());
// Check that K is no longer present.
assertThat(inspector.clazz(K.class), not(isPresent()));
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/ImplementsMergedTypeIndirectlyTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/ImplementsMergedTypeIndirectlyTest.java
index 2b76994..2f59834 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/ImplementsMergedTypeIndirectlyTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/ImplementsMergedTypeIndirectlyTest.java
@@ -63,6 +63,7 @@
// Verify that TestClass still implements J.
ClassSubject testClassSubject = inspector.clazz(TestClass.class);
- assertEquals(J.class.getTypeName(), testClassSubject.getDexClass().interfaces.toSourceString());
+ assertEquals(
+ J.class.getTypeName(), testClassSubject.getDexProgramClass().interfaces.toSourceString());
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedFieldTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedFieldTypeTest.java
index fd4a6a7..7ea419c 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedFieldTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedFieldTypeTest.java
@@ -102,7 +102,8 @@
if (enableVerticalClassMerging) {
// Verify that SuperTestClass has been merged into TestClass.
assertThat(inspector.clazz(SuperTestClass.class), not(isPresent()));
- assertEquals("java.lang.Object", testClassSubject.getDexClass().superType.toSourceString());
+ assertEquals(
+ "java.lang.Object", testClassSubject.getDexProgramClass().superType.toSourceString());
// Verify that TestClass.field has been removed.
assertEquals(1, testClassSubject.allFields().size());
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedParameterTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedParameterTypeTest.java
index 99897fc..3961905 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedParameterTypeTest.java
@@ -104,7 +104,8 @@
if (enableVerticalClassMerging) {
// Verify that SuperTestClass has been merged into TestClass.
assertThat(inspector.clazz(SuperTestClass.class), not(isPresent()));
- assertEquals("java.lang.Object", testClassSubject.getDexClass().superType.toSourceString());
+ assertEquals(
+ "java.lang.Object", testClassSubject.getDexProgramClass().superType.toSourceString());
// Verify that TestClass.method has been removed.
List<FoundMethodSubject> methods =
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedReturnTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedReturnTypeTest.java
index 47f1c08..9173e66 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedReturnTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedReturnTypeTest.java
@@ -120,7 +120,8 @@
if (enableVerticalClassMerging) {
// Verify that SuperTestClass has been merged into TestClass.
assertThat(inspector.clazz(SuperTestClass.class), not(isPresent()));
- assertEquals("java.lang.Object", testClassSubject.getDexClass().superType.toSourceString());
+ assertEquals(
+ "java.lang.Object", testClassSubject.getDexProgramClass().superType.toSourceString());
// Verify that TestClass.method has been removed.
List<FoundMethodSubject> methods =
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
index e736ea7..09c1356 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -865,13 +865,13 @@
CodeInspector inspector = new CodeInspector(processedApplication);
ClassSubject clazz = inspector.clazz(OutlineOptions.CLASS_NAME);
assertTrue(clazz.isPresent());
- assertEquals(3, clazz.getDexClass().directMethods().size());
+ assertEquals(3, clazz.getDexProgramClass().directMethods().size());
// Collect the return types of the putlines for the body of method1 and method2.
List<DexType> r = new ArrayList<>();
- for (int i = 0; i < clazz.getDexClass().directMethods().size(); i++) {
- if (clazz.getDexClass().directMethods().get(i).getCode().asDexCode().instructions[0]
+ for (int i = 0; i < clazz.getDexProgramClass().directMethods().size(); i++) {
+ if (clazz.getDexProgramClass().directMethods().get(i).getCode().asDexCode().instructions[0]
instanceof InvokeVirtual) {
- r.add(clazz.getDexClass().directMethods().get(i).method.proto.returnType);
+ r.add(clazz.getDexProgramClass().directMethods().get(i).method.proto.returnType);
}
}
assert r.size() == 2;
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
index 6aad725..36c7766 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -112,7 +112,7 @@
CodeInspector inspector = new CodeInspector(application);
ClassSubject clazz = inspector.clazz(className);
assertTrue(clazz.isPresent());
- return clazz.getDexClass();
+ return clazz.getDexProgramClass();
}
protected DexClass getClass(DexApplication application, MethodSignature signature) {
@@ -124,7 +124,7 @@
CodeInspector inspector = new CodeInspector(appPath);
ClassSubject clazz = inspector.clazz(className);
assertTrue(clazz.isPresent());
- return clazz.getDexClass();
+ return clazz.getDexProgramClass();
} catch (IOException | ExecutionException e) {
throw new RuntimeException(e);
}
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 7b0e410..3f1ad69 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -28,6 +28,7 @@
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
+import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
@@ -336,6 +337,15 @@
});
}
+ public ClassFileTransformer setAnnotation() {
+ return setAccessFlags(
+ accessFlags -> {
+ assert accessFlags.isAbstract();
+ assert accessFlags.isInterface();
+ accessFlags.setAnnotation();
+ });
+ }
+
public ClassFileTransformer setBridge(Method method) {
return setAccessFlags(method, MethodAccessFlags::setBridge);
}
@@ -401,6 +411,16 @@
boolean test(int access, String name, String descriptor, String signature, String[] exceptions);
}
+ public ClassFileTransformer removeInnerClasses() {
+ return addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public void visitInnerClass(String name, String outerName, String innerName, int access) {
+ // Intentionally empty.
+ }
+ });
+ }
+
public ClassFileTransformer removeMethods(MethodPredicate predicate) {
return addClassTransformer(
new ClassTransformer() {
@@ -414,7 +434,7 @@
});
}
- /** Abstraction of the MethodVisitor.visitMethodInsn method with its continuation. */
+ /** Abstraction of the MethodVisitor.visitMethodInsn method with its sub visitor. */
@FunctionalInterface
public interface MethodInsnTransform {
void visitMethodInsn(
@@ -423,40 +443,38 @@
String name,
String descriptor,
boolean isInterface,
- MethodInsnTransformContinuation continuation);
+ MethodVisitor visitor);
}
- /** Continuation for transforming a method. Will continue with the super visitor if called. */
- @FunctionalInterface
- public interface MethodInsnTransformContinuation {
- void apply(int opcode, String owner, String name, String descriptor, boolean isInterface);
- }
-
- /** Abstraction of the MethodVisitor.visitTypeInsn method with its continuation. */
+ /** Abstraction of the MethodVisitor.visitTypeInsn method with its sub visitor. */
@FunctionalInterface
public interface TypeInsnTransform {
- void visitTypeInsn(int opcode, String type, TypeInsnTransformContinuation continuation);
+ void visitTypeInsn(int opcode, String type, MethodVisitor visitor);
}
- /** Continuation for transforming a method. Will continue with the super visitor if called. */
+ /** Abstraction of the MethodVisitor.visitLdcInsn method with its sub visitor. */
@FunctionalInterface
- public interface TypeInsnTransformContinuation {
- void apply(int opcode, String type);
+ public interface LdcInsnTransform {
+ void visitLdcInsn(Object value, MethodVisitor visitor);
}
+ /** Abstraction of the MethodVisitor.visitTryCatchBlock method with its sub visitor. */
@FunctionalInterface
public interface TryCatchBlockTransform {
void visitTryCatchBlock(
- Label start,
- Label end,
- Label handler,
- String type,
- TryCatchBlockTransformContinuation continuation);
+ Label start, Label end, Label handler, String type, MethodVisitor visitor);
}
- @FunctionalInterface
- public interface TryCatchBlockTransformContinuation {
- void apply(Label start, Label end, Label handler, String type);
+ public ClassFileTransformer replaceAnnotationDescriptor(
+ String oldDescriptor, String newDescriptor) {
+ return addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+ return super.visitAnnotation(
+ descriptor.equals(oldDescriptor) ? newDescriptor : descriptor, visible);
+ }
+ });
}
public ClassFileTransformer replaceClassDescriptorInMethodInstructions(
@@ -508,6 +526,23 @@
});
}
+ @FunctionalInterface
+ private interface VisitMethodInsnCallback {
+ void visitMethodInsn(
+ int opcode, String owner, String name, String descriptor, boolean isInterface);
+ }
+
+ private MethodVisitor redirectVisitMethodInsn(
+ MethodVisitor visitor, VisitMethodInsnCallback callback) {
+ return new MethodVisitor(ASM7, visitor) {
+ @Override
+ public void visitMethodInsn(
+ int opcode, String owner, String name, String descriptor, boolean isInterface) {
+ callback.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ }
+ };
+ }
+
public ClassFileTransformer transformMethodInsnInMethod(
String methodName, MethodInsnTransform transform) {
return addMethodTransformer(
@@ -517,7 +552,12 @@
int opcode, String owner, String name, String descriptor, boolean isInterface) {
if (getContext().method.getMethodName().equals(methodName)) {
transform.visitMethodInsn(
- opcode, owner, name, descriptor, isInterface, super::visitMethodInsn);
+ opcode,
+ owner,
+ name,
+ descriptor,
+ isInterface,
+ redirectVisitMethodInsn(this, super::visitMethodInsn));
} else {
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
@@ -525,6 +565,21 @@
});
}
+ @FunctionalInterface
+ private interface VisitTypeInsnCallback {
+ void visitTypeInsn(int opcode, String type);
+ }
+
+ private MethodVisitor redirectVisitTypeInsn(
+ MethodVisitor visitor, VisitTypeInsnCallback callback) {
+ return new MethodVisitor(ASM7, visitor) {
+ @Override
+ public void visitTypeInsn(int opcode, String type) {
+ callback.visitTypeInsn(opcode, type);
+ }
+ };
+ }
+
public ClassFileTransformer transformTypeInsnInMethod(
String methodName, TypeInsnTransform transform) {
return addMethodTransformer(
@@ -532,7 +587,8 @@
@Override
public void visitTypeInsn(int opcode, String type) {
if (getContext().method.getMethodName().equals(methodName)) {
- transform.visitTypeInsn(opcode, type, super::visitTypeInsn);
+ transform.visitTypeInsn(
+ opcode, type, redirectVisitTypeInsn(this, super::visitTypeInsn));
} else {
super.visitTypeInsn(opcode, type);
}
@@ -540,6 +596,21 @@
});
}
+ @FunctionalInterface
+ private interface VisitTryCatchBlockCallback {
+ void visitTryCatchBlock(Label start, Label end, Label handler, String type);
+ }
+
+ private MethodVisitor redirectVistiTryCatchBlock(
+ MethodVisitor visitor, VisitTryCatchBlockCallback callback) {
+ return new MethodVisitor(ASM7, visitor) {
+ @Override
+ public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+ callback.visitTryCatchBlock(start, end, handler, type);
+ }
+ };
+ }
+
public ClassFileTransformer transformTryCatchBlock(
String methodName, TryCatchBlockTransform transform) {
return addMethodTransformer(
@@ -547,7 +618,12 @@
@Override
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
if (getContext().method.getMethodName().equals(methodName)) {
- transform.visitTryCatchBlock(start, end, handler, type, super::visitTryCatchBlock);
+ transform.visitTryCatchBlock(
+ start,
+ end,
+ handler,
+ type,
+ redirectVistiTryCatchBlock(this, super::visitTryCatchBlock));
} else {
super.visitTryCatchBlock(start, end, handler, type);
}
@@ -555,16 +631,18 @@
});
}
- /** Abstraction of the MethodVisitor.visitLdcInsn method with its continuation. */
@FunctionalInterface
- public interface LdcInsnTransform {
- void visitLdcInsn(Object value, LdcInsnTransformContinuation continuation);
+ private interface VisitLdcInsnCallback {
+ void visitLdcInsn(Object value);
}
- /** Continuation for transforming a method. Will continue with the super visitor if called. */
- @FunctionalInterface
- public interface LdcInsnTransformContinuation {
- void apply(Object value);
+ private MethodVisitor redirectVisitLdcInsn(MethodVisitor visitor, VisitLdcInsnCallback callback) {
+ return new MethodVisitor(ASM7, visitor) {
+ @Override
+ public void visitLdcInsn(Object value) {
+ callback.visitLdcInsn(value);
+ }
+ };
}
public ClassFileTransformer transformLdcInsnInMethod(
@@ -574,7 +652,7 @@
@Override
public void visitLdcInsn(Object value) {
if (getContext().method.getMethodName().equals(methodName)) {
- transform.visitLdcInsn(value, super::visitLdcInsn);
+ transform.visitLdcInsn(value, redirectVisitLdcInsn(this, super::visitLdcInsn));
} else {
super.visitLdcInsn(value);
}
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 8786f30..92e0e10 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
@@ -5,7 +5,7 @@
package com.android.tools.r8.utils.codeinspector;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexProgramClass;
import java.util.List;
import java.util.function.Consumer;
import kotlinx.metadata.jvm.KotlinClassMetadata;
@@ -73,7 +73,7 @@
}
@Override
- public DexClass getDexClass() {
+ public DexProgramClass getDexProgramClass() {
return null;
}
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 c8c6142..a234c36 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
@@ -4,7 +4,7 @@
package com.android.tools.r8.utils.codeinspector;
-import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.TypeReference;
@@ -156,7 +156,7 @@
return dump.toString();
}
- public abstract DexClass getDexClass();
+ public abstract DexProgramClass getDexProgramClass();
public abstract AnnotationSubject annotation(String name);
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 fc5c38f..00d00ca 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
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.naming.ClassNamingForNameMapper;
@@ -221,8 +222,9 @@
}
@Override
- public DexClass getDexClass() {
- return dexClass;
+ public DexProgramClass getDexProgramClass() {
+ assert dexClass.isProgramClass();
+ return dexClass.asProgramClass();
}
public ClassSubject getSuperClass() {
@@ -334,7 +336,7 @@
}
public TypeSubject asTypeSubject() {
- return new TypeSubject(codeInspector, getDexClass().type);
+ return new TypeSubject(codeInspector, getDexProgramClass().type);
}
@Override
@@ -348,7 +350,7 @@
codeInspector.getFactory().kotlin, annotationSubject.getAnnotation());
assertTrue(metadata instanceof KotlinClassMetadata.Class);
KotlinClassMetadata.Class kClass = (KotlinClassMetadata.Class) metadata;
- return new FoundKmClassSubject(codeInspector, getDexClass(), kClass.toKmClass());
+ return new FoundKmClassSubject(codeInspector, getDexProgramClass(), kClass.toKmClass());
}
@Override
@@ -364,11 +366,11 @@
|| metadata instanceof KotlinClassMetadata.MultiFileClassPart);
if (metadata instanceof KotlinClassMetadata.FileFacade) {
KotlinClassMetadata.FileFacade kFile = (KotlinClassMetadata.FileFacade) metadata;
- return new FoundKmPackageSubject(codeInspector, getDexClass(), kFile.toKmPackage());
+ return new FoundKmPackageSubject(codeInspector, getDexProgramClass(), kFile.toKmPackage());
} else {
KotlinClassMetadata.MultiFileClassPart kPart =
(KotlinClassMetadata.MultiFileClassPart) metadata;
- return new FoundKmPackageSubject(codeInspector, getDexClass(), kPart.toKmPackage());
+ return new FoundKmPackageSubject(codeInspector, getDexProgramClass(), kPart.toKmPackage());
}
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java
index a2185b0..51d7152 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmClassSubject.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.utils.codeinspector;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.kotlin.KotlinMetadataWriter;
import com.android.tools.r8.utils.DescriptorUtils;
import java.util.List;
import java.util.Objects;
@@ -127,4 +128,11 @@
public CodeInspector getCodeInspector() {
return codeInspector;
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ KotlinMetadataWriter.appendKmClass("", sb, kmClass);
+ return sb.toString();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmPackageSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmPackageSubject.java
index d2c4fa6..4c1f418 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmPackageSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmPackageSubject.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.utils.codeinspector;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.kotlin.KotlinMetadataWriter;
import kotlinx.metadata.KmDeclarationContainer;
import kotlinx.metadata.KmPackage;
@@ -50,4 +51,11 @@
// from scratch.
return false;
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ KotlinMetadataWriter.appendKmPackage("", sb, kmPackage);
+ return sb.toString();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmTypeParameterSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmTypeParameterSubject.java
index fb5e909..c8057ce 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmTypeParameterSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundKmTypeParameterSubject.java
@@ -69,7 +69,7 @@
}
for (int i = 0; i < kmTypeParameter.getUpperBounds().size(); i++) {
if (!KmTypeSubject.areEqual(
- kmTypeParameter.getUpperBounds().get(i), other.getUpperBounds().get(i))) {
+ kmTypeParameter.getUpperBounds().get(i), other.getUpperBounds().get(i), true)) {
return false;
}
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index fe574e4..782bbf0 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -229,6 +229,9 @@
@Override
public boolean hasLocalVariableTable() {
Code code = getMethod().getCode();
+ if (code == null) {
+ return false;
+ }
if (code.isDexCode()) {
DexCode dexCode = code.asDexCode();
if (dexCode.getDebugInfo() != null) {
@@ -254,6 +257,9 @@
@Override
public LineNumberTable getLineNumberTable() {
Code code = getMethod().getCode();
+ if (code == null) {
+ return null;
+ }
if (code.isDexCode()) {
return getDexLineNumberTable(code.asDexCode());
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeProjectionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeProjectionSubject.java
index 76f3782..3ee66c9 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeProjectionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeProjectionSubject.java
@@ -57,6 +57,6 @@
if (one.getVariance() != other.getVariance()) {
return false;
}
- return KmTypeSubject.areEqual(one.getType(), other.getType());
+ return KmTypeSubject.areEqual(one.getType(), other.getType(), true);
}
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java
index 44a141a..c9975a3 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/KmTypeSubject.java
@@ -84,10 +84,17 @@
if (!(obj instanceof KmTypeSubject)) {
return false;
}
- return areEqual(this.kmType, ((KmTypeSubject) obj).kmType);
+ return areEqual(this.kmType, ((KmTypeSubject) obj).kmType, true);
}
- public static boolean areEqual(KmType one, KmType other) {
+ public boolean equalUpToAbbreviatedType(KmTypeSubject other) {
+ if (other == null) {
+ return false;
+ }
+ return areEqual(this.kmType, other.kmType, false);
+ }
+
+ public static boolean areEqual(KmType one, KmType other, boolean checkAbbreviatedType) {
if (one == null && other == null) {
return true;
}
@@ -109,10 +116,11 @@
return false;
}
}
- if (!areEqual(one.getAbbreviatedType(), other.getAbbreviatedType())) {
+ if (checkAbbreviatedType
+ && !areEqual(one.getAbbreviatedType(), other.getAbbreviatedType(), checkAbbreviatedType)) {
return false;
}
- if (!areEqual(one.getOuterType(), other.getOuterType())) {
+ if (!areEqual(one.getOuterType(), other.getOuterType(), checkAbbreviatedType)) {
return false;
}
// TODO(b/152745540): Add equality for flexibleUpperBoundType.
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/TypeSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/TypeSubject.java
index ae59c47..357c777 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/TypeSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/TypeSubject.java
@@ -41,7 +41,7 @@
}
public boolean is(ClassSubject type) {
- return dexType == type.getDexClass().type;
+ return dexType == type.getDexProgramClass().type;
}
@Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/analysis/ProtoApplicationStats.java b/src/test/java/com/android/tools/r8/utils/codeinspector/analysis/ProtoApplicationStats.java
index 28075e6..4d4c84b 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/analysis/ProtoApplicationStats.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/analysis/ProtoApplicationStats.java
@@ -147,7 +147,7 @@
private void computeStats() {
for (FoundClassSubject classSubject : inspector.allClasses()) {
DexType originalType = classSubject.getOriginalDexType(dexItemFactory);
- if (classSubject.getDexClass().isEnum()) {
+ if (classSubject.getDexProgramClass().isEnum()) {
enumStats.enums.add(originalType);
}
diff --git a/third_party/chrome/chrome_200430.tar.gz.sha1 b/third_party/chrome/chrome_200430.tar.gz.sha1
new file mode 100644
index 0000000..75c0b5a
--- /dev/null
+++ b/third_party/chrome/chrome_200430.tar.gz.sha1
@@ -0,0 +1 @@
+2c2b6b64f0b073effe22a5048b53ef69dd3a56fc
\ No newline at end of file
diff --git a/tools/chrome_data.py b/tools/chrome_data.py
index cf62ce3..81f2fc7 100644
--- a/tools/chrome_data.py
+++ b/tools/chrome_data.py
@@ -5,9 +5,11 @@
import os
import utils
+ANDROID_N_API = '24'
BASE = os.path.join(utils.THIRD_PARTY, 'chrome')
V180917_BASE = os.path.join(BASE, 'chrome_180917_ffbaa8')
+V200430_BASE = os.path.join(BASE, 'chrome_200430')
INPUT_JARS = [
'out/Release/gen/chrome/android/monochrome_public_apk/monochrome_public_apk.jar',
@@ -246,4 +248,12 @@
'libraries': [os.path.join(V180917_BASE, path) for path in LIBRARIES],
},
},
+ '200430': {
+ 'deploy' : {
+ 'inputs': [os.path.join(V200430_BASE, 'program.jar')],
+ 'pgconf': [os.path.join(V200430_BASE, 'proguard.config')],
+ 'libraries': [os.path.join(V200430_BASE, 'library.jar')],
+ 'min-api': ANDROID_N_API
+ },
+ },
}
diff --git a/tools/r8_release.py b/tools/r8_release.py
index acc70ed..ba87676 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -457,8 +457,9 @@
if not args.use_existing_work_branch:
clients = subprocess.check_output('g4 myclients', shell=True)
if ':%s:' % client_name in clients:
- print ("Remove the existing '%s' client before continuing, " +
- "or use option --use-existing-work-branch.") % client_name
+ print ("Remove the existing '%s' client before continuing " +
+ "(force delete: 'g4 citc -d -f %s'), " +
+ "or use option --use-existing-work-branch.") % (client_name, client_name)
sys.exit(1)
diff --git a/tools/test.py b/tools/test.py
index a75beb4..d6e4983 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -240,6 +240,8 @@
gradle_args.append('--no-daemon')
# Build an R8 with dependencies for bootstrapping tests before adding test sources.
+ gradle_args.append('r8WithDeps')
+ gradle_args.append('r8WithDeps11')
gradle_args.append('r8WithRelocatedDeps')
gradle_args.append('r8WithRelocatedDeps11')